From 1b336e8348d8704b5d1a1e3b282d1ae337375157 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Tue, 9 Aug 2022 13:36:12 +0200 Subject: [PATCH] Use URL encoding for authentication header values --- .../client/util/AuthorizationHeaderBuilder.kt | 27 +++++-------------- .../util/AuthorizationHeaderBuilderTests.kt | 16 ++++++----- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/jellyfin-api/src/commonMain/kotlin/org/jellyfin/sdk/api/client/util/AuthorizationHeaderBuilder.kt b/jellyfin-api/src/commonMain/kotlin/org/jellyfin/sdk/api/client/util/AuthorizationHeaderBuilder.kt index 08e4e095e..5dfa6c9d7 100644 --- a/jellyfin-api/src/commonMain/kotlin/org/jellyfin/sdk/api/client/util/AuthorizationHeaderBuilder.kt +++ b/jellyfin-api/src/commonMain/kotlin/org/jellyfin/sdk/api/client/util/AuthorizationHeaderBuilder.kt @@ -1,15 +1,13 @@ package org.jellyfin.sdk.api.client.util +import io.ktor.http.encodeURLParameter + public object AuthorizationHeaderBuilder { public const val AUTHORIZATION_SCHEME: String = "MediaBrowser" - public fun encodeParameterValue(raw: String): String = raw - // Trim whitespace - .trim() - // Remove characters not allowed in HTTP headers - .replace(Regex("""[^\x20-\x7e]"""), "?") - // Remove characters that might break serverside parsing - .replace(Regex("""[=,"]"""), "?") + public fun encodeParameterValue(raw: String): String = raw.encodeURLParameter( + spaceToPlus = true + ) public fun buildParameter(key: String, value: String): String { // Check for bad strings to prevent endless hours debugging why the server throws http 500 errors @@ -22,18 +20,9 @@ public object AuthorizationHeaderBuilder { require(!key.startsWith('"') && !key.endsWith('"')) { "Key $key can not start or end with the \" character in the authorization header" } - require(!value.contains('=')) { - "Value $value (for key $key) can not contain the = character in the authorization header" - } - require(!value.contains(',')) { - "Value $value (for key $key) can not contain the , character in the authorization header" - } - require(!value.startsWith('"') && !value.endsWith('"')) { - "Value $value (for key $key) can not start or end with the \" character in the authorization header" - } // key="value" - return """${key}="$value"""" + return """${key}="${encodeParameterValue(value)}"""" } public fun buildHeader( @@ -47,9 +36,7 @@ public object AuthorizationHeaderBuilder { "Client" to clientName, "Version" to clientVersion, "DeviceId" to deviceId, - // Only encode the device name as it is user input - // other fields should be validated manually by the client - "Device" to encodeParameterValue(deviceName), + "Device" to deviceName, "Token" to accessToken ) diff --git a/jellyfin-api/src/commonTest/kotlin/org/jellyfin/sdk/api/client/util/AuthorizationHeaderBuilderTests.kt b/jellyfin-api/src/commonTest/kotlin/org/jellyfin/sdk/api/client/util/AuthorizationHeaderBuilderTests.kt index 56b5481fb..4a8a137df 100644 --- a/jellyfin-api/src/commonTest/kotlin/org/jellyfin/sdk/api/client/util/AuthorizationHeaderBuilderTests.kt +++ b/jellyfin-api/src/commonTest/kotlin/org/jellyfin/sdk/api/client/util/AuthorizationHeaderBuilderTests.kt @@ -1,16 +1,20 @@ package org.jellyfin.sdk.api.client.util import io.kotest.core.spec.style.FunSpec +import io.kotest.data.forAll +import io.kotest.data.row import io.kotest.matchers.shouldBe class AuthorizationHeaderBuilderTests : FunSpec({ test("encodeParameter removes special characters") { - AuthorizationHeaderBuilder.encodeParameterValue("""test""") shouldBe "test" - AuthorizationHeaderBuilder.encodeParameterValue("""test+""") shouldBe "test+" - AuthorizationHeaderBuilder.encodeParameterValue("""'test'""") shouldBe "'test'" - AuthorizationHeaderBuilder.encodeParameterValue("""今日は""") shouldBe "???" - AuthorizationHeaderBuilder.encodeParameterValue("""水母""") shouldBe "??" - AuthorizationHeaderBuilder.encodeParameterValue("""ἈᾼᾺΆᾍᾋ""") shouldBe "??????" + forAll( + row("""test""", "test"), + row("""test+""", "test%2B"), + row("""'test'""", "%27test%27"), + row("""今日は""", "%E4%BB%8A%E6%97%A5%E3%81%AF"), + row("""水母""", "%E6%B0%B4%E6%AF%8D"), + row("""ἈᾼᾺΆᾍᾋ""", "%E1%BC%88%E1%BE%BC%E1%BE%BA%E1%BE%BB%E1%BE%8D%E1%BE%8B"), + ) { a, b -> AuthorizationHeaderBuilder.encodeParameterValue(a) shouldBe b } } test("buildParameter creates a valid header with access token") {