|
3 | 3 | to CMR Access Control to get token-to-user mappings, extracting tokens from
|
4 | 4 | request headers, and defining caching keys and related tasks."
|
5 | 5 | (:require
|
| 6 | + [buddy.core.keys :as buddy-keys] |
| 7 | + [buddy.sign.jwt :as jwt] |
| 8 | + [cheshire.core :as json] |
6 | 9 | [clojure.data.xml :as xml]
|
| 10 | + [clojure.string :as string] |
| 11 | + [cmr.authz.components.config :as config] |
7 | 12 | [cmr.authz.http :as http]
|
8 | 13 | [cmr.http.kit.request :as request]
|
9 | 14 | [cmr.http.kit.response :as response]
|
10 | 15 | [taoensso.timbre :as log]
|
11 | 16 | [xml-in.core :as xml-in]))
|
12 | 17 |
|
| 18 | + |
13 | 19 | (def token-info-resource
|
14 |
| - "The path segment to the ECHO REST API resource that is queried |
15 |
| - in order to get user/token mappings." |
16 |
| - "/tokens/get_token_info") |
| 20 | + "EDL Launchpad token verification resource." |
| 21 | + "/api/nams/edl_user") |
| 22 | + |
| 23 | +(def client-token-resource |
| 24 | + "EDL client bearer token resource." |
| 25 | + "/oauth/token?grant_type=client_credentials") |
17 | 26 |
|
18 | 27 | (defn token-data-key
|
19 | 28 | "Generate a key to be used for caching token data."
|
|
50 | 59 | [xml-str]
|
51 | 60 | (find-xml xml-str [:token_info :token]))
|
52 | 61 |
|
| 62 | +(defn parse-bearer-token |
| 63 | + [body] |
| 64 | + (:access_token (json/parse-string body true))) |
| 65 | + |
53 | 66 | (defn parse-username
|
54 | 67 | "Parse the XML that is returned when querying the CMR Access Control API for
|
55 | 68 | the username associated with the token."
|
|
58 | 71 | (find-xml [:token_info :user_name])
|
59 | 72 | first))
|
60 | 73 |
|
| 74 | +(defn parse-lp-username |
| 75 | + [body] |
| 76 | + (:uid (json/parse-string body true))) |
| 77 | + |
| 78 | +(defn add-basic-auth |
| 79 | + ([user pass] |
| 80 | + (add-basic-auth {} user pass)) |
| 81 | + ([req user pass] |
| 82 | + (assoc req :basic-auth [user pass]))) |
| 83 | + |
| 84 | +(defn get-client-token |
| 85 | + "Get the CMR client bearer token from from the cache." |
| 86 | + [system] |
| 87 | + (let [base-url (config/get-edl-rest-url system) |
| 88 | + url (str base-url client-token-resource)] |
| 89 | + (request/async-post |
| 90 | + url |
| 91 | + (-> {} |
| 92 | + (request/add-accept "application/json") |
| 93 | + (request/add-basic-auth (config/get-edl-username system) (config/get-edl-password system))) |
| 94 | + {} |
| 95 | + #(response/general-response-handler % response/error-handler parse-bearer-token)))) |
| 96 | + |
| 97 | +(defn verify-edl-token-locally |
| 98 | + "Uses the EDL public key to verify jwt tokens locally." |
| 99 | + [system token] |
| 100 | + (try |
| 101 | + (log/trace "edl public key= " (config/get-jwt-public-key system)) |
| 102 | + (let [public-key (buddy-keys/jwk->public-key (json/parse-string (config/get-jwt-public-key system) true)) |
| 103 | + bearer-stripped-token (string/replace token #"Bearer\W+" "") |
| 104 | + decrypted-token (jwt/unsign bearer-stripped-token public-key {:alg :rs256})] |
| 105 | + {:body (:uid decrypted-token)}) |
| 106 | + (catch Exception ex |
| 107 | + (log/error (.getMessage ex)) |
| 108 | + (log/debug "JWT local token verification failed, trying launchpad instead.")))) |
| 109 | + |
61 | 110 | (defn get-token-info
|
62 | 111 | "Query the CMR Access Control API for information assocated with the given
|
63 | 112 | token."
|
64 |
| - [base-url token] |
65 |
| - (let [url (str base-url token-info-resource) |
66 |
| - data (str "id=" token)] |
67 |
| - (log/trace "Making token-info query to ECHO REST:" url) |
68 |
| - (request/async-post |
69 |
| - url |
70 |
| - (-> {:body data} |
71 |
| - (http/add-options) |
72 |
| - (request/add-token-header token) |
73 |
| - (request/add-form-ct)) |
74 |
| - {} |
75 |
| - #(response/body-only-response-handler % response/error-handler parse-username)))) |
| 113 | + [system token] |
| 114 | + (let [base-url (config/get-edl-rest-url system) |
| 115 | + url (str base-url token-info-resource)] |
| 116 | + (log/trace "Decoding jwt token to get token info") |
| 117 | + (if-let [token-info (verify-edl-token-locally system token)] |
| 118 | + (do |
| 119 | + (log/trace "user info found for jwt token, token-info=" token-info) |
| 120 | + (deliver (promise) token-info)) |
| 121 | + (let [bearer-token @(get-client-token system)] |
| 122 | + (if (:errors bearer-token) |
| 123 | + (deliver (promise) bearer-token) |
| 124 | + (do |
| 125 | + (log/trace "retrieved cmr client token= " bearer-token) |
| 126 | + (log/trace "user info not found for jwt token, attempting to authenticate user via launchpad") |
| 127 | + (request/async-post |
| 128 | + url |
| 129 | + (-> {:body (str "token=" token)} |
| 130 | + (http/add-options) |
| 131 | + (request/add-token-header (str "Bearer " (:body bearer-token)))) |
| 132 | + {} |
| 133 | + #(response/general-response-handler % response/error-handler parse-lp-username)))))))) |
76 | 134 |
|
77 | 135 | (defn ->user
|
78 | 136 | "Given a token, return the associated user name."
|
79 |
| - [base-url token] |
80 |
| - (let [result @(get-token-info base-url token) |
| 137 | + [system token] |
| 138 | + (let [result @(get-token-info system token) |
81 | 139 | errors (:errors result)]
|
| 140 | + (log/trace "result=" result) |
82 | 141 | (if errors
|
83 | 142 | (throw (ex-info (first errors) result))
|
84 |
| - result))) |
| 143 | + (:body result)))) |
0 commit comments