mirror of
https://github.com/cirruslabs/tart.git
synced 2026-10-17 19:36:10 +02:00
Trim whitespace around WWW-Authenticate directives (#1377)
RFC 7235 allows optional whitespace around the commas that separate auth-params, e.g. `Bearer realm="...", service="...", scope="..."`. The parser kept that whitespace in the keys, so "service" and "scope" were stored as " service" and " scope" and were never sent to the token endpoint.
This commit is contained in:
@@ -27,8 +27,11 @@ class WWWAuthenticate {
|
||||
+ "key=value or key=\"value\"")
|
||||
}
|
||||
|
||||
let key = String(parts[0])
|
||||
var value = String(parts[1])
|
||||
// Directives can be surrounded by optional whitespace[1]
|
||||
//
|
||||
// [1]: https://www.rfc-editor.org/rfc/rfc7235#section-4.1
|
||||
let key = parts[0].trimmingCharacters(in: .whitespaces)
|
||||
var value = parts[1].trimmingCharacters(in: .whitespaces)
|
||||
value = value.trimmingCharacters(in: CharacterSet(charactersIn: "\""))
|
||||
|
||||
kvs[key] = value
|
||||
|
||||
@@ -20,6 +20,10 @@ final class RegistryRealmTests: XCTestCase {
|
||||
try await checkAuthentication(mode: "encoded")
|
||||
}
|
||||
|
||||
func testChallengeWithSpacesAfterCommas() async throws {
|
||||
try await checkAuthentication(mode: "spaced")
|
||||
}
|
||||
|
||||
private func checkAuthentication(mode: String) async throws {
|
||||
let server = try RealmTokenServer(mode: mode)
|
||||
defer { server.stop() }
|
||||
@@ -83,7 +87,7 @@ private final class RealmTokenServer {
|
||||
if !request.lowercased().contains("authorization: bearer test-token\r\n") {
|
||||
let query: String
|
||||
switch self.mode {
|
||||
case "plain": query = ""
|
||||
case "plain", "spaced": query = ""
|
||||
case "encoded": query = "?tenant=alpha%2Bbeta&escape=%7e%2f&mode=one&mode=two&empty="
|
||||
default: query = "?tenant=alpha%26beta&mode=one&mode=two&empty="
|
||||
}
|
||||
@@ -91,7 +95,8 @@ private final class RealmTokenServer {
|
||||
(self.mode == "encoded" ? "&sc%6Fpe=old&service=old" : "")
|
||||
let realm = "http://127.0.0.1:\(self.listener.port!.rawValue)/token\(query)\(stale)"
|
||||
status = "401 Unauthorized"
|
||||
extraHeaders = "WWW-Authenticate: Bearer realm=\"\(realm)\",service=\"registry\",scope=\"repository:org/repo:pull\"\r\n"
|
||||
let separator = self.mode == "spaced" ? ", " : ","
|
||||
extraHeaders = "WWW-Authenticate: Bearer realm=\"\(realm)\"\(separator)service=\"registry\"\(separator)scope=\"repository:org/repo:pull\"\r\n"
|
||||
}
|
||||
} else if url.path == "/token" {
|
||||
let receivedURL = URLComponents(url: url, resolvingAgainstBaseURL: false)!
|
||||
@@ -102,7 +107,7 @@ private final class RealmTokenServer {
|
||||
values("tenant") == ["alpha+beta"] && values("escape") == ["~/"]
|
||||
let normalValid = values("tenant") == ["alpha&beta"]
|
||||
let valid = values("service") == ["registry"] && values("scope") == ["repository:org/repo:pull"] &&
|
||||
(self.mode == "plain" || ((self.mode == "encoded" ? encodedValid : normalValid) &&
|
||||
(self.mode == "plain" || self.mode == "spaced" || ((self.mode == "encoded" ? encodedValid : normalValid) &&
|
||||
values("mode") == ["one", "two"] && values("empty") == [""]))
|
||||
status = valid ? "200 OK" : "400 Bad Request"
|
||||
body = valid ? "{\"token\":\"test-token\"}" : "{\"error\":\"missing or changed realm parameters\"}"
|
||||
|
||||
@@ -23,6 +23,20 @@ final class WWWAuthenticateTests: XCTestCase {
|
||||
XCTAssertEqual(["a": "b", "c": "d"], wwwAuthenticate.kvs)
|
||||
}
|
||||
|
||||
func testWhitespaceAroundDirectives() throws {
|
||||
// RFC 7235 allows optional whitespace around the commas separating directives[1]
|
||||
//
|
||||
// [1]: https://www.rfc-editor.org/rfc/rfc7235#section-4.1
|
||||
let wwwAuthenticate = try WWWAuthenticate(rawHeaderValue: "Bearer realm=\"https://auth.example.com/token\", service=\"registry.example.com\" , scope=\"repository:org/repo:pull,push\"")
|
||||
|
||||
XCTAssertEqual("Bearer", wwwAuthenticate.scheme)
|
||||
XCTAssertEqual([
|
||||
"realm": "https://auth.example.com/token",
|
||||
"service": "registry.example.com",
|
||||
"scope": "repository:org/repo:pull,push",
|
||||
], wwwAuthenticate.kvs)
|
||||
}
|
||||
|
||||
func testIncompleteHeader() throws {
|
||||
XCTAssertThrowsError(try WWWAuthenticate(rawHeaderValue: "Whatever")) {
|
||||
XCTAssertTrue($0 is RegistryError)
|
||||
|
||||
Reference in New Issue
Block a user