diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/AuthenticationManager.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/AuthenticationManager.kt index 1f5ee5ad3e85c41fd5dfe592ccbae89c83395e71..33da1663dcac054e9a535861a9e7534b2476f70c 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/AuthenticationManager.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/AuthenticationManager.kt @@ -9,14 +9,14 @@ class AuthenticationManager @Inject constructor() { fun canOwnerAccess(credentials: Credentials): Boolean { // TODO check credentials: // * token must be from owner - // * if credentials.mailboxId is not null, must have accessType right to mailboxId + // * if credentials.folderId is not null, must have accessType right to folderId return credentials.token == "test123" } fun canOwnerOrContactAccess(credentials: Credentials): Boolean { - require(credentials.mailboxId != null) + require(credentials.folderId != null) // TODO check credentials: - // * token must have credentials.accessType right to mailboxId + // * token must have credentials.accessType right to folderId return credentials.token == "test123" || credentials.token == "test124" } diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/BearerAuthenticationProvider.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/BearerAuthenticationProvider.kt index 22330145be34f24d3a7b3a862586e08d69bbd4cd..cb5bef86c58fb4ef676124ebd59d94b533420196 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/BearerAuthenticationProvider.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/BearerAuthenticationProvider.kt @@ -12,42 +12,42 @@ import io.ktor.auth.Principal import io.ktor.auth.UnauthorizedResponse import io.ktor.auth.parseAuthorizationHeader import io.ktor.http.auth.HttpAuthHeader -import io.ktor.request.ApplicationRequest import io.ktor.request.httpMethod import io.ktor.response.respond import org.briarproject.mailbox.core.util.LogUtils.debug import org.slf4j.LoggerFactory.getLogger -private val BearerAuthKey: Any = "BearerAuth" +private val AUTH_KEY_BEARER: Any = "BearerAuth" private val LOG = getLogger(BearerAuthenticationProvider::class.java) internal class BearerAuthenticationProvider constructor(config: Configuration) : AuthenticationProvider(config) { internal var realm: String = config.realm ?: "Ktor Server" - internal val authHeader: (ApplicationCall) -> HttpAuthHeader? = config.authHeader + internal val authHeader: (ApplicationCall) -> HttpAuthHeader? = { call -> + try { + call.request.parseAuthorizationHeader() + } catch (ex: IllegalArgumentException) { + LOG.warn("Illegal HTTP auth header", ex) + null + } + } internal val authenticationFunction = config.authenticationFunction internal class Configuration internal constructor(name: String?) : AuthenticationProvider.Configuration(name) { var realm: String? = null - val authHeader: (ApplicationCall) -> HttpAuthHeader? = { call -> - call.request.parseAuthorizationHeaderOrNull() - } - var authenticationFunction: AuthenticationFunction<Credentials> = { - throw NotImplementedError( - "Bearer auth validate function is not specified." + - "Use bearer { validate { ... } } to fix." - ) - } /** - * Apply [validate] function to every call with [String] + * This function is applied to every call with [Credentials]. * @return a principal (usually an instance of [Principal]) or `null` */ - fun validate(validate: suspend ApplicationCall.(Credentials) -> Principal?) { - authenticationFunction = validate + var authenticationFunction: AuthenticationFunction<Credentials> = { + throw NotImplementedError( + "Bearer auth authenticationFunction is not specified." + + "Use bearer { authenticationFunction = { ... } } to fix." + ) } internal fun build() = BearerAuthenticationProvider(this) @@ -78,12 +78,13 @@ internal fun Authentication.Configuration.bearer( context.unauthorizedResponse(AuthenticationFailedCause.InvalidCredentials, provider) return@intercept } - val mailboxId = call.parameters["mailboxId"] + val folderId = call.parameters["folderId"] - LOG.debug("name: $name") - LOG.debug("httpMethod: ${call.request.httpMethod}") + // TODO remove logging before release + LOG.debug { "name: $name" } + LOG.debug { "httpMethod: ${call.request.httpMethod}" } - val credentials = Credentials(accessType, token, mailboxId) + val credentials = Credentials(accessType, token, folderId) val principal = provider.authenticationFunction(call, credentials) if (principal == null) { context.unauthorizedResponse(AuthenticationFailedCause.InvalidCredentials, provider) @@ -93,7 +94,7 @@ internal fun Authentication.Configuration.bearer( } catch (cause: Throwable) { val message = cause.message ?: cause.javaClass.simpleName LOG.debug { "Bearer verification failed: $message" } - context.error(BearerAuthKey, AuthenticationFailedCause.Error(message)) + context.error(AUTH_KEY_BEARER, AuthenticationFailedCause.Error(message)) } } register(provider) @@ -103,7 +104,7 @@ private fun AuthenticationContext.unauthorizedResponse( cause: AuthenticationFailedCause, provider: BearerAuthenticationProvider, ) { - challenge(BearerAuthKey, cause) { + challenge(AUTH_KEY_BEARER, cause) { call.respond( UnauthorizedResponse( HttpAuthHeader.Parameterized( @@ -117,10 +118,3 @@ private fun AuthenticationContext.unauthorizedResponse( } } } - -private fun ApplicationRequest.parseAuthorizationHeaderOrNull() = try { - parseAuthorizationHeader() -} catch (ex: IllegalArgumentException) { - LOG.warn("Illegal HTTP auth header", ex) - null -} diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Credentials.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Credentials.kt index 55c63079341959304fb46f5d9e03110e879b8e95..c06a49fee85f3f80d35c5c3a4d404bbaaedc919c 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Credentials.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Credentials.kt @@ -5,7 +5,7 @@ import io.ktor.http.HttpMethod data class Credentials( val accessType: AccessType, val token: String, - val mailboxId: String?, + val folderId: String?, ) enum class AccessType { UPLOAD, DOWNLOAD_DELETE } diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Routing.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Routing.kt index c48ac4de9c5dd74a8347873fed6c4614f8d19d41..cfe68937d74b72f4d49993d250df4224151e1c17 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Routing.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/Routing.kt @@ -14,7 +14,7 @@ import io.ktor.routing.put import io.ktor.routing.route import io.ktor.routing.routing -internal const val V = "/" // TODO set to "/v1" for release +internal const val V = "" // TODO set to "/v1" for release internal fun Application.configureBasicApi() = routing { route("$V/") { @@ -59,19 +59,19 @@ internal fun Application.configureContactApi() = routing { internal fun Application.configureFilesApi() = routing { authenticate(AuthContext.ownerAndContacts) { - route("$V/files/{mailboxId}") { + route("$V/files/{folderId}") { post { call.respond( HttpStatusCode.OK, "post: Not yet implemented. " + - "mailboxId: ${call.parameters["mailboxId"]}" + "folderId: ${call.parameters["folderId"]}" ) } get { call.respond( HttpStatusCode.OK, "get: Not yet implemented. " + - "mailboxId: ${call.parameters["mailboxId"]}" + "folderId: ${call.parameters["folderId"]}" ) } route("/{fileId}") { @@ -79,7 +79,7 @@ internal fun Application.configureFilesApi() = routing { call.respond( HttpStatusCode.OK, "get: Not yet implemented. " + - "mailboxId: ${call.parameters["mailboxId"]} " + + "folderId: ${call.parameters["folderId"]} " + "fileId: ${call.parameters["fileId"]}" ) } @@ -87,7 +87,7 @@ internal fun Application.configureFilesApi() = routing { call.respond( HttpStatusCode.OK, "delete: Not yet implemented. " + - "mailboxId: ${call.parameters["mailboxId"]} " + + "folderId: ${call.parameters["folderId"]} " + "fileId: ${call.parameters["fileId"]}" ) } diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt index 86ac2825d9cb21158ee3afc3ec6ca941b3c07000..a806bc3b380832e66529174ac7e6a0c900c6f504 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/server/WebServerManager.kt @@ -7,11 +7,17 @@ import io.ktor.features.CallLogging import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty import org.briarproject.mailbox.core.lifecycle.Service +import org.briarproject.mailbox.core.server.WebServerManager.Companion.PORT +import org.slf4j.Logger import org.slf4j.LoggerFactory.getLogger import javax.inject.Inject import javax.inject.Singleton -interface WebServerManager : Service +interface WebServerManager : Service { + companion object { + const val PORT: Int = 8000 + } +} @Singleton internal class WebServerManagerImpl @Inject constructor( @@ -19,18 +25,18 @@ internal class WebServerManagerImpl @Inject constructor( ) : WebServerManager { internal companion object { - internal const val PORT = 8000 - private val LOG = getLogger(WebServerManager::class.java) + private val LOG: Logger = getLogger(WebServerManager::class.java) } private val server by lazy { embeddedServer(Netty, PORT, watchPaths = emptyList()) { install(CallLogging) - // TODO validate mailboxId and fileId somewhere + // TODO validate folderId and fileId somewhere install(Authentication) { bearer(AuthContext.ownerOnly) { realm = "Briar Mailbox Owner" - validate { credentials -> + authenticationFunction = { credentials -> + // TODO: Remove logging [of credentials] before release. LOG.error("credentials: $credentials") if (authManager.canOwnerAccess(credentials)) { UserIdPrincipal(AuthContext.ownerOnly) @@ -39,12 +45,12 @@ internal class WebServerManagerImpl @Inject constructor( } bearer(AuthContext.ownerAndContacts) { realm = "Briar Mailbox" - validate { credentials -> + authenticationFunction = { credentials -> LOG.error("credentials: $credentials") - val mailboxId = credentials.mailboxId - // we must have a mailboxId for this AuthContext - if (mailboxId == null) { - LOG.warn("No mailboxId found in request") + val folderId = credentials.folderId + // we must have a folderId for this AuthContext + if (folderId == null) { + LOG.warn("No folderId found in request") null } else if (authManager.canOwnerOrContactAccess(credentials)) { UserIdPrincipal(AuthContext.ownerAndContacts) diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java index 88285497c5717b971c7a599355cffc2900beeeeb..05929a6ef0dd4fdd7bcbf6fb2eaab759ddc7c1bc 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java @@ -1,25 +1,5 @@ package org.briarproject.mailbox.core.tor; -import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; -import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY; -import static org.briarproject.mailbox.core.tor.TorConstants.CONTROL_PORT; -import static org.briarproject.mailbox.core.tor.TorPlugin.State.ACTIVE; -import static org.briarproject.mailbox.core.tor.TorPlugin.State.DISABLED; -import static org.briarproject.mailbox.core.tor.TorPlugin.State.ENABLING; -import static org.briarproject.mailbox.core.tor.TorPlugin.State.INACTIVE; -import static org.briarproject.mailbox.core.tor.TorPlugin.State.STARTING_STOPPING; -import static org.briarproject.mailbox.core.util.IoUtils.copyAndClose; -import static org.briarproject.mailbox.core.util.IoUtils.tryToClose; -import static org.briarproject.mailbox.core.util.LogUtils.info; -import static org.briarproject.mailbox.core.util.LogUtils.logException; -import static org.briarproject.mailbox.core.util.LogUtils.warn; -import static org.briarproject.mailbox.core.util.PrivacyUtils.scrubOnion; -import static org.slf4j.LoggerFactory.getLogger; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; -import static java.util.Objects.requireNonNull; - import net.freehaven.tor.control.EventHandler; import net.freehaven.tor.control.TorControlConnection; @@ -30,7 +10,7 @@ import org.briarproject.mailbox.core.event.EventListener; import org.briarproject.mailbox.core.lifecycle.IoExecutor; import org.briarproject.mailbox.core.lifecycle.Service; import org.briarproject.mailbox.core.lifecycle.ServiceException; -import org.briarproject.mailbox.core.server.WebServerManagerImpl; +import org.briarproject.mailbox.core.server.WebServerManager; import org.briarproject.mailbox.core.settings.Settings; import org.briarproject.mailbox.core.settings.SettingsManager; import org.briarproject.mailbox.core.system.Clock; @@ -248,7 +228,7 @@ abstract class TorPlugin implements Service, EventHandler, EventListener { // Check whether we're online updateConnectionStatus(networkManager.getNetworkStatus()); // Create a hidden service if necessary - ioExecutor.execute(() -> publishHiddenService(String.valueOf(WebServerManagerImpl.PORT))); + ioExecutor.execute(() -> publishHiddenService(String.valueOf(WebServerManager.PORT))); } private boolean assetsAreUpToDate() { diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/WebServerIntegrationTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/WebServerIntegrationTest.kt index 904455c82cd6e74d58b9ec02654066958c82b086..753642437efe430a862737084eff97296ec2cf33 100644 --- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/WebServerIntegrationTest.kt +++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/WebServerIntegrationTest.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.runBlocking import org.briarproject.mailbox.core.DaggerTestComponent import org.briarproject.mailbox.core.TestComponent import org.briarproject.mailbox.core.TestModule -import org.briarproject.mailbox.core.server.WebServerManagerImpl.Companion.PORT +import org.briarproject.mailbox.core.server.WebServerManager.Companion.PORT import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test