From 9ebe49b85d503580976bf0551e8eaf04e82e6a30 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 15 Oct 2019 15:37:32 -0300 Subject: [PATCH] [headless] Let websocket upgrade requests pass in AccessManager This is because JavaScript in browsers apparently can not add Authentication headers to websocket requests, so we use a dedicated authentication message there. In Javalin 3, the AccessManager also handles websocket requests. We need to let those pass to support JavaScript. --- briar-headless/build.gradle | 2 +- .../org/briarproject/briar/headless/Router.kt | 30 +++++++++++-------- briar-headless/witness.gradle | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/briar-headless/build.gradle b/briar-headless/build.gradle index 0fa3905a3..32dcec66f 100644 --- a/briar-headless/build.gradle +++ b/briar-headless/build.gradle @@ -14,7 +14,7 @@ dependencies { implementation project(path: ':briar-core', configuration: 'default') implementation project(path: ':bramble-java', configuration: 'default') - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.40' + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.40' implementation 'io.javalin:javalin:3.5.0' implementation 'org.slf4j:slf4j-simple:1.7.26' implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.0' diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt index 69e14af0e..940eac8fe 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/Router.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonParseException import com.fasterxml.jackson.databind.ObjectMapper import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.* +import io.javalin.core.security.AccessManager import io.javalin.core.util.Header.AUTHORIZATION import io.javalin.http.BadRequestResponse import io.javalin.http.Context @@ -23,6 +24,9 @@ import javax.inject.Inject import javax.inject.Singleton import kotlin.system.exitProcess +private const val VERSION = "v1" +private const val WS = "/$VERSION/ws" + @Immutable @Singleton internal class Router @@ -39,27 +43,29 @@ constructor( private val logger = getLogger(Router::javaClass.name) private val stopped = AtomicBoolean(false) - internal fun start(authToken: String, port: Int, debug: Boolean) : Javalin { + internal fun start(authToken: String, port: Int, debug: Boolean): Javalin { briarService.start() getRuntime().addShutdownHook(Thread(this::stop)) + val accessManager = AccessManager { handler, ctx, _ -> + when { + ctx.header(AUTHORIZATION) == "Bearer $authToken" -> handler.handle(ctx) + ctx.matchedPath() == WS -> handler.handle(ctx) // websockets use their own auth + else -> ctx.status(401).result("Unauthorized") + } + } + val app = Javalin.create { config -> config.showJavalinBanner = false - config.accessManager { handler, ctx, _ -> - if (ctx.header(AUTHORIZATION) == "Bearer $authToken") { - handler.handle(ctx) - } else { - ctx.status(401).result("Unauthorized") - } - } + config.accessManager(accessManager) if (debug) config.enableDevLogging() - }.events {event -> + }.events { event -> event.serverStartFailed { serverStopped() } event.serverStopped { serverStopped() } } app.routes { - path("/v1") { + path("/$VERSION") { path("/contacts") { get { ctx -> contactController.list(ctx) } path("add") { @@ -92,7 +98,7 @@ constructor( } } } - app.ws("/v1/ws") { ws -> + app.ws(WS) { ws -> if (logger.isLoggable(INFO)) ws.onConnect { ctx -> logger.info("Received websocket connection from ${ctx.session.remoteAddress}") logger.info("Waiting for authentication") @@ -147,7 +153,7 @@ fun Context.getContactIdFromPathParam(): ContactId { /** * Returns a String from the JSON field or throws [BadRequestResponse] if null or empty. */ -fun Context.getFromJson(objectMapper: ObjectMapper, field: String) : String { +fun Context.getFromJson(objectMapper: ObjectMapper, field: String): String { try { val jsonNode = objectMapper.readTree(body()) if (!jsonNode.hasNonNull(field)) throw BadRequestResponse("'$field' missing in JSON") diff --git a/briar-headless/witness.gradle b/briar-headless/witness.gradle index 4fb258d68..f7d48f626 100644 --- a/briar-headless/witness.gradle +++ b/briar-headless/witness.gradle @@ -77,7 +77,7 @@ dependencyVerification { 'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.40:kotlin-stdlib-common-1.3.40.jar:5f551a3ffe7683f4741d96fdc49835864aa08ddfc63d38109884973e19eed1cb', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50:kotlin-stdlib-common-1.3.50.jar:8ce678e88e4ba018b66dacecf952471e4d7dfee156a8a819760a5a5ff29d323c', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.40:kotlin-stdlib-jdk7-1.3.40.jar:f79d84613679095d17518dd8fb4249183f334cf80e4d7a25a7e0ed519ee993ab', - 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.31:kotlin-stdlib-jdk8-1.3.31.jar:ad6acd219b468a532ac3b3c5aacbfd5db02d0ffcf967e2113e4677e2429490f6', + 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.40:kotlin-stdlib-jdk8-1.3.40.jar:05e11f693719aa515e6946f51eacc88cb53437cc685d914c114f090792ba9ba1', 'org.jetbrains.kotlin:kotlin-stdlib:1.3.40:kotlin-stdlib-1.3.40.jar:f76f9812a703ba5085af8f51769e60e8ecd5e99b55b2ced097cf2343e972ad7b', 'org.jetbrains.kotlin:kotlin-stdlib:1.3.50:kotlin-stdlib-1.3.50.jar:e6f05746ee0366d0b52825a090fac474dcf44082c9083bbb205bd16976488d6c', 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1:kotlinx-coroutines-core-1.1.1.jar:ac423f8a0aa4b4e74529696ff82c0171f81a8c8ab182a1965dff25e69c1f7844',