From 280c6308f901e97bed8d1011208bf788b63bf6e3 Mon Sep 17 00:00:00 2001 From: Joost Diepenmaat Date: Mon, 30 Mar 2026 12:51:22 +0200 Subject: [PATCH 1/2] WIP: easier interceptors for noodlebar & logging --- connector/README.md | 54 ++++++++++++- .../org/bdinetwork/connector/interceptors.clj | 80 +++++++++++++++++++ 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/connector/README.md b/connector/README.md index 918a8f1..ef5485b 100644 --- a/connector/README.md +++ b/connector/README.md @@ -146,7 +146,7 @@ When it fails to connect to the downstream server, respond with Example: ```edn -[proxy "https://example.com"] +[proxy (str "https://example.com" (get request :uri))] ``` Note: this interceptor should always be the last in the list of @@ -319,6 +319,39 @@ Provide access to the last `:n-of-lines` (defaults to 100) lines of `:json-file` --- +`[org.bdinetwork.connector.interceptors/logger & [additional-props]]` + +Short name: `bdi/logger` + +Log incoming requests, response status and duration. + +Also logs BDI specific information: "client", +"delegation-evidence", "delegation-issues", "delegation-mask". + +Optional `additional-props` will be evaluated in the "leave" +phase and logged as diagnostic context, `props` should be a shallow +map with string keys. + +Example log messsage: + +``` +GET http://localhost:8081/ HTTP/1.1 / 200 OK / 370ms +``` + +Example with MDC: + +```edn +[bdi/logger {"ua" (get-in request [:headers "user-agent"])}] +``` + +Example log message: + +``` +GET http://localhost:8080/ HTTP/1.1 / 200 OK / 123ms status=200, uri="/", ua="curl/1.2.3" +``` + +--- + `[org.bdinetwork.connector.interceptors/noodlebar-delegation]` Short name: `bdi/noodlebar-delegation` @@ -329,6 +362,23 @@ not match the delegation mask. --- +`[org.bdinetwork.connector.interceptors/noodlebar-validate-policy {:keys [policy-issuer resource-type resource-identifier resource-attribute action]}]` + +Short name: `bdi/noodlebar-validate-policy` + +Retrieves and evaluates delegation evidence for request. +Responds with 403 Forbidden when the evidence is not found or does +not match the delegation mask. + +Derives some information from the request's Bearer token claims: + +The policy's target must match the bearer-token's :organizationId claim +The policy's service-provider must match the :aud claim + +Required + +--- + `[(org.bdinetwork.connector.interceptors/set-bearer-token) {:keys [server-id base-url client-id private-key x5c association-id association-url path]}]` Short name: `bdi/set-bearer-token` @@ -408,7 +458,7 @@ The following example is protected by a basic authentication username / password [request update :headers assoc "authorization" #join ["Basic " #b64 #join [#env! "BACKEND_USER" ":" #env! "BACKEND_PASS"]]] [response update :headers assoc "x-bdi-connector" "passed"] - [proxy "http://backend:port/"]]} + [proxy (str "http://backend:port/" (get request :uri))]]} {:match {} :interceptors [[logger] diff --git a/connector/src/org/bdinetwork/connector/interceptors.clj b/connector/src/org/bdinetwork/connector/interceptors.clj index 0287462..d1ca656 100644 --- a/connector/src/org/bdinetwork/connector/interceptors.clj +++ b/connector/src/org/bdinetwork/connector/interceptors.clj @@ -15,6 +15,7 @@ [org.bdinetwork.ishare.client :as ishare-client] [org.bdinetwork.ishare.client.request :as ishare-request] [org.bdinetwork.ishare.client.validate-delegation :as validate-delegation] + [passage.interceptors :as passage] [passage.response :as response] [ring.middleware.json :as ring-json] [ring.middleware.params :as ring-params]) @@ -261,3 +262,82 @@ (assoc :response (-> response/forbidden (assoc-in [:headers "content-type"] "application/json") (assoc :body (json/json-str {:delegation-issues issues})))))))}) + +(def ^{:interceptor true + :expr-arglist '[{:keys [policy-issuer resource-type resource-identifier resource-attribute action]}]} + noodlebar-validate-policy + "Retrieves and evaluates delegation evidence for request. + Responds with 403 Forbidden when the evidence is not found or does + not match the delegation mask. + + Derives some information from the request's Bearer token claims: + + The policy's target must match the bearer-token's :organizationId claim + The policy's service-provider must match the :aud claim + + Required +" + (update noodlebar-delegation :enter + (fn noodlebar-policy-enter [enter] + (fn [ctx base-request {:keys [policy-issuer resource-type resource-identifier resource-attribute action]}] + (enter ctx + base-request + {:policyIssuer policy-issuer + :target {:accessSubject (get-in ctx [:oauth2/bearer-token-claims :organizationId])} + :policySets + [{:policies + [{:rules [{:effect "Permit"}] + :target + {:resource {:type (or resource-type "") + :identifiers [resource-identifier] + :attributes [resource-attribute]} + :actions [action] + :environment + {:serviceProviders [(get-in ctx [:oauth2/bearer-token-claims :aud])]}}}]}]}))))) + + + + +(def ^{:interceptor true + :expr-arglist '[& [additional-props]]} + logger + "Log incoming requests, response status and duration. + + Also logs BDI specific information: \"client\", + \"delegation-evidence\", \"delegation-issues\", \"delegation-mask\". + + Optional `additional-props` will be evaluated in the \"leave\" + phase and logged as diagnostic context, `props` should be a shallow + map with string keys. + + Example log messsage: + + ``` + GET http://localhost:8081/ HTTP/1.1 / 200 OK / 370ms + ``` + + Example with MDC: + + ```edn + [bdi/logger {\"ua\" (get-in request [:headers \"user-agent\"])}] + ``` + + Example log message: + + ``` + GET http://localhost:8080/ HTTP/1.1 / 200 OK / 123ms status=200, uri=\"/\", ua=\"curl/1.2.3\" + ```" + (-> passage/logger + (update :leave + (fn [leave] + (fn logger-leave + ([{:keys [request response] :as ctx} additional-props] + (leave (merge {"uri" (get request :uri) + "status" (get response :status) + "client" (get-in ctx [:oauth2/bearer-token-claims :sub]) + "delegation-issues" (get ctx :delegation-issues) + "delegation-evidence" (get ctx :delegation-evidence) + "delegation-mask" (get ctx :delegation-mask)} + additional-props))) + ([ctx] + (logger-leave ctx nil))))))) From 84d7a5e7000d37294d74a6a4887aa3a531387ed9 Mon Sep 17 00:00:00 2001 From: Joost Diepenmaat Date: Mon, 30 Mar 2026 14:09:50 +0200 Subject: [PATCH 2/2] fixup --- connector/README.md | 5 ++-- .../org/bdinetwork/connector/interceptors.clj | 26 +++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/connector/README.md b/connector/README.md index ef5485b..ed74b46 100644 --- a/connector/README.md +++ b/connector/README.md @@ -362,7 +362,7 @@ not match the delegation mask. --- -`[org.bdinetwork.connector.interceptors/noodlebar-validate-policy {:keys [policy-issuer resource-type resource-identifier resource-attribute action]}]` +`[org.bdinetwork.connector.interceptors/noodlebar-validate-policy {:keys [policy-issuer resource-type resource-identifier resource-attributes action]}]` Short name: `bdi/noodlebar-validate-policy` @@ -375,7 +375,8 @@ Derives some information from the request's Bearer token claims: The policy's target must match the bearer-token's :organizationId claim The policy's service-provider must match the :aud claim -Required +Required keys: +policy-issuer, resource-type, resource-identifier, resource-attributes, action --- diff --git a/connector/src/org/bdinetwork/connector/interceptors.clj b/connector/src/org/bdinetwork/connector/interceptors.clj index d1ca656..d48c73a 100644 --- a/connector/src/org/bdinetwork/connector/interceptors.clj +++ b/connector/src/org/bdinetwork/connector/interceptors.clj @@ -264,7 +264,7 @@ (assoc :body (json/json-str {:delegation-issues issues})))))))}) (def ^{:interceptor true - :expr-arglist '[{:keys [policy-issuer resource-type resource-identifier resource-attribute action]}]} + :expr-arglist '[{:keys [policy-issuer resource-type resource-identifier resource-attributes action]}]} noodlebar-validate-policy "Retrieves and evaluates delegation evidence for request. Responds with 403 Forbidden when the evidence is not found or does @@ -275,11 +275,11 @@ The policy's target must match the bearer-token's :organizationId claim The policy's service-provider must match the :aud claim - Required -" + Required keys: + policy-issuer, resource-type, resource-identifier, resource-attributes, action" (update noodlebar-delegation :enter (fn noodlebar-policy-enter [enter] - (fn [ctx base-request {:keys [policy-issuer resource-type resource-identifier resource-attribute action]}] + (fn [ctx base-request {:keys [policy-issuer resource-type resource-identifier resource-attributes action]}] (enter ctx base-request {:policyIssuer policy-issuer @@ -288,9 +288,9 @@ [{:policies [{:rules [{:effect "Permit"}] :target - {:resource {:type (or resource-type "") + {:resource {:type resource-type :identifiers [resource-identifier] - :attributes [resource-attribute]} + :attributes resource-attributes} :actions [action] :environment {:serviceProviders [(get-in ctx [:oauth2/bearer-token-claims :aud])]}}}]}]}))))) @@ -332,12 +332,12 @@ (fn [leave] (fn logger-leave ([{:keys [request response] :as ctx} additional-props] - (leave (merge {"uri" (get request :uri) - "status" (get response :status) - "client" (get-in ctx [:oauth2/bearer-token-claims :sub]) - "delegation-issues" (get ctx :delegation-issues) - "delegation-evidence" (get ctx :delegation-evidence) - "delegation-mask" (get ctx :delegation-mask)} - additional-props))) + (leave ctx (merge {"uri" (get request :uri) + "status" (get response :status) + "client" (get-in ctx [:oauth2/bearer-token-claims :sub]) + "delegation-issues" (get ctx :delegation-issues) + "delegation-evidence" (get ctx :delegation-evidence) + "delegation-mask" (get ctx :delegation-mask)} + additional-props))) ([ctx] (logger-leave ctx nil)))))))