[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.
This commit is contained in:
Torsten Grote
2019-10-15 15:37:32 -03:00
parent 6f153f14c7
commit 9ebe49b85d
3 changed files with 20 additions and 14 deletions

View File

@@ -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'

View File

@@ -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")

View File

@@ -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',