From e848e513e835b36414abdcba881ac4fed18331fd Mon Sep 17 00:00:00 2001 From: Dennis Schridde Date: Fri, 10 Apr 2026 18:14:29 +0200 Subject: [PATCH] Make r.m.authorization/parse-authorization public Make the existing ring.middleware.authorization/parse-authorization function public and make it take the Authorization header value as input. According to RFC 7235 Section 2 and RFC 9110 Section 11, the value credentials of the Authorization HTTP request header has the same structure as each of the comma-separated challenges of the WWW-Authenticate HTTP response header, which allows to reuse this function also for parsing responses: * https://datatracker.ietf.org/doc/html/rfc7235#section-2 * https://datatracker.ietf.org/doc/html/rfc9110#section-11 --- src/ring/middleware/authorization.clj | 14 ++++-- test/ring/middleware/authorization_test.clj | 47 +++++++++++---------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/ring/middleware/authorization.clj b/src/ring/middleware/authorization.clj index f42361d..8334080 100644 --- a/src/ring/middleware/authorization.clj +++ b/src/ring/middleware/authorization.clj @@ -16,10 +16,16 @@ {} (str/split auth-params #"\s*,\s*"))) -(defn- parse-authorization [request] +(defn parse-credentials + "Parse credentials as used in the Authorization header of an HTTP + request. + + Note: The WWW-Authenticate header of an HTTP response contains a + comma-separated list of challenges, which each happen to have the same + structure as the single credentials in the Authorization header." + [credentials] (when-let [[auth-scheme token-or-params] - (some-> (get-in request [:headers "authorization"]) - (str/split #"\s" 2))] + (some-> credentials (str/split #"\s" 2))] (cond (empty? token-or-params) {:scheme (str/lower-case auth-scheme)} @@ -37,7 +43,7 @@ [request] (if (:authorization request) request - (assoc request :authorization (parse-authorization request)))) + (assoc request :authorization (parse-credentials (get-in request [:headers "authorization"]))))) (defn wrap-authorization "Parses the Authorization header in the request map, then assocs the result diff --git a/test/ring/middleware/authorization_test.clj b/test/ring/middleware/authorization_test.clj index 99f170d..bfc31a5 100644 --- a/test/ring/middleware/authorization_test.clj +++ b/test/ring/middleware/authorization_test.clj @@ -2,43 +2,44 @@ (:require [clojure.test :refer :all] [ring.middleware.authorization :refer :all])) -(deftest test-authorization-request - (testing "pre-existing authorization" - (is (= "TEST" - (-> {:headers {"authorization" "Basic"} - :authorization "TEST"} - authorization-request - :authorization)))) +(deftest test-parse-authorization (testing "no authorization" - (is (nil? (-> {:headers {}} - authorization-request - :authorization)))) + (is (nil? (parse-credentials nil)))) (testing "scheme without token" (is (= {:scheme "basic"} - (-> {:headers {"authorization" "Basic"}} - authorization-request - :authorization)))) + (parse-credentials "Basic")))) (testing "scheme with zero-length token" (is (= {:scheme "basic"} - (-> {:headers {"authorization" "Basic "}} - authorization-request - :authorization)))) + (parse-credentials "Basic ")))) (testing "token68" (is (= {:scheme "basic" :token "dGVzdA=="} - (-> {:headers {"authorization" "Basic dGVzdA=="}} - authorization-request - :authorization)))) + (parse-credentials "Basic dGVzdA==")))) (testing "auth-params, some malformed" (is (= {:scheme "digest" :params {"a" "B" "c" "d" "eeee" "dGVzdA==" - "k" "1"}} - (-> {:headers {"authorization" "Digest A=B, c=\"d\", - eeee=\"dGVzdA==\", fparam=dGVzdA==, g, \"h\"=i, =j, = ,, , k=1"}} + "k" "1" + "l" "234"}} + (parse-credentials "Digest A=B, c=\"d\", + eeee=\"dGVzdA==\", fparam=dGVzdA==, g, \"h\"=i, =j, = ,, , k=1, l = \"234\""))))) + +(deftest test-authorization-request + (testing "pre-existing authorization" + (is (= "TEST" + (-> {:headers {"authorization" "Basic"} + :authorization "TEST"} authorization-request - :authorization))))) + :authorization)))) + (testing "no authorization" + (is (nil? (-> {:headers {}} + authorization-request + :authorization)))) + (testing "with authorization" + (is (some? (-> {:headers {"authorization" "Basic"}} + authorization-request + :authorization))))) (deftest test-wrap-authorization-none (let [handler (wrap-authorization (fn [req respond _] (respond req)))