Skip to content

Commit 90dfd26

Browse files
committed
CMR-9125 refactors user token lookup in ous to use EDL
1 parent 04c25ab commit 90dfd26

File tree

14 files changed

+153
-40
lines changed

14 files changed

+153
-40
lines changed

other/cmr-exchange/authz/project.clj

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
:url "https://github.com/cmr-exchange/authz"
44
:license {:name "Apache License, Version 2.0"
55
:url "http://www.apache.org/licenses/LICENSE-2.0"}
6-
:dependencies [[cheshire "5.8.1"]
6+
:dependencies [[buddy/buddy-sign "3.4.333"]
7+
[cheshire "5.8.1"]
78
[clojusc/trifl "0.4.2"]
89
[clojusc/twig "0.4.0"]
910
[com.stuartsierra/component "0.3.2"]

other/cmr-exchange/authz/resources/config/cmr-authz/config.edn

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
{:auth-caching
2-
{:init {}
2+
{:jwt-public-key "{
3+
\"kty\": \"RSA\",
4+
\"n\": \"3LopSyeoSZZGorSPjk4mMbR0ybVSLvfrONGSGXCNXE6ScX9Y1QC_zV8fVeh4XO8tYDi9CgzqK3Nhjsd5KI0ZzTI8Lf52tyr7OzebZXGZpMvyvdp59wlbPL4WFkIHvWFpgypSrTQIRENKaYW_yQB9srq6JpUx14aRG5TpiBuPqgnGM-qBqPvLq5LX9kVhqbV46TuZd9uPn_gISut7A7K3Y5S24DZd3ebxXPap1cn6-mIY30QG5oYmVlMZxdVPhnTzjj4ZNsfyKSRKq3F_UapEr4ynhr-ONgj8HyozyFqTpUn3o8pKAMVaOEfZmRlqb3jnQknbcsJ9fNxmWUUZr_PysQ\",
5+
\"e\": \"AQAB\",
6+
\"kid\": \"edljwtpubkey_sit\"
7+
}"
8+
:init {}
39
:ttl
410
{:minutes 60}
511
:lru
@@ -13,6 +19,10 @@
1319
:relative
1420
{:root
1521
{:url "/access-control"}}}}
22+
:edl
23+
{:rest
24+
{:protocol "https"
25+
:host "sit.urs.earthdata.nasa.gov"}}
1626
:echo
1727
{:rest
1828
{:protocol "https"

other/cmr-exchange/authz/src/cmr/authz/components/config.clj

+15-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
;; The URLs returned by these functions have no trailing slash:
2929
(def get-access-control-url #(get-service-url % :access-control))
3030
(def get-echo-rest-url #(get-service-url % :echo-rest))
31+
(def get-edl-rest-url #(get-service-url % :edl-rest))
3132

3233
(defn cache-dumpfile
3334
[system]
@@ -45,13 +46,25 @@
4546
[system]
4647
(* (get-in (get-cfg system) [:auth-caching :ttl :minutes]) ; minutes
4748
60 ; seconds
48-
1000 ; milliseconds
49-
))
49+
1000)) ; milliseconds
5050

5151
(defn cache-type
5252
[system]
5353
(get-in (get-cfg system) [:auth-caching :type]))
5454

55+
(defn get-jwt-public-key
56+
[system]
57+
(get-in (get-cfg system) [:auth-caching :jwt-public-key]))
58+
59+
(defn get-edl-username
60+
[system]
61+
(get-in (get-cfg system) [:cmr :edl :username]))
62+
63+
(defn get-edl-password
64+
[system]
65+
(get-in (get-cfg system) [:cmr :edl :password]))
66+
67+
5568
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5669
;;; Component Lifecycle Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5770
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

other/cmr-exchange/authz/src/cmr/authz/config.clj

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020

2121
(or (= service :echo)
2222
(= service :echo-rest))
23-
[:echo :rest]))
23+
[:echo :rest]
24+
25+
(or (= service :edl)
26+
(= service :edl-rest))
27+
[:edl :rest]))
2428

2529
(defn service->base-url
2630
[service]

other/cmr-exchange/authz/src/cmr/authz/errors.clj

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
(def error-code 403)
66
(def no-permissions "You do not have permissions to access that resource.")
7-
(def token-required "An ECHO token is required to access this resource.")
7+
(def token-required "A valid token is required to access this resource.")
88

99
(def status-map
1010
"This is a lookup data structure for how HTTP status/error codes map to CMR

other/cmr-exchange/authz/src/cmr/authz/token.clj

+77-18
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,26 @@
33
to CMR Access Control to get token-to-user mappings, extracting tokens from
44
request headers, and defining caching keys and related tasks."
55
(:require
6+
[buddy.core.keys :as buddy-keys]
7+
[buddy.sign.jwt :as jwt]
8+
[cheshire.core :as json]
69
[clojure.data.xml :as xml]
10+
[clojure.string :as string]
11+
[cmr.authz.components.config :as config]
712
[cmr.authz.http :as http]
813
[cmr.http.kit.request :as request]
914
[cmr.http.kit.response :as response]
1015
[taoensso.timbre :as log]
1116
[xml-in.core :as xml-in]))
1217

18+
1319
(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")
1726

1827
(defn token-data-key
1928
"Generate a key to be used for caching token data."
@@ -50,6 +59,10 @@
5059
[xml-str]
5160
(find-xml xml-str [:token_info :token]))
5261

62+
(defn parse-bearer-token
63+
[body]
64+
(:access_token (json/parse-string body true)))
65+
5366
(defn parse-username
5467
"Parse the XML that is returned when querying the CMR Access Control API for
5568
the username associated with the token."
@@ -58,27 +71,73 @@
5871
(find-xml [:token_info :user_name])
5972
first))
6073

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+
61110
(defn get-token-info
62111
"Query the CMR Access Control API for information assocated with the given
63112
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))))))))
76134

77135
(defn ->user
78136
"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)
81139
errors (:errors result)]
140+
(log/trace "result=" result)
82141
(if errors
83142
(throw (ex-info (first errors) result))
84-
result)))
143+
(:body result))))

other/cmr-exchange/http-kit-support/src/cmr/http/kit/request.clj

+6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
([req field value]
2323
(assoc-in req [:headers field] value)))
2424

25+
(defn add-basic-auth
26+
([user pass]
27+
(add-basic-auth {} user pass))
28+
([req user pass]
29+
(assoc req :basic-auth [user pass])))
30+
2531
(defn add-accept
2632
([value]
2733
(add-accept {} value))

other/cmr-exchange/http-kit-support/src/cmr/http/kit/response.clj

+3-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@
6464

6565
(defn json-errors
6666
[body]
67-
(:errors (:body (parse-json-result body))))
67+
(or (:errors (parse-json-result body))
68+
(seq (remove nil? [(:error (parse-json-result body))]))))
6869

6970
(defn parse-xml-body
7071
[body]
@@ -174,6 +175,7 @@
174175
(log/debug "Handling client response ...")
175176
(log/trace "headers:" headers)
176177
(log/trace "body:" body)
178+
(log/trace "status:" status)
177179
(cond error
178180
(do
179181
(log/error error)

other/cmr-exchange/metadata-proxy/src/cmr/metadata/proxy/components/auth.clj

+3-3
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
(caching/lookup
5656
system
5757
(token/user-id-key token)
58-
#(token/->user (config/get-echo-rest-url system) token))
58+
#(token/->user system token))
5959
(catch Exception e
6060
(log/error e)
6161
{:errors (base-errors/exception-data e)})))
@@ -131,7 +131,7 @@
131131
(if-let [user-token (token/extract request)]
132132
(let [user-lookup (cached-user system user-token)
133133
errors (:errors user-lookup)]
134-
(log/debug "ECHO token provided; proceeding ...")
134+
(log/debug "Token provided; proceeding ...")
135135
(log/trace "user-lookup:" user-lookup)
136136
(if errors
137137
(do
@@ -156,7 +156,7 @@
156156
user-token
157157
user-lookup)))))
158158
(do
159-
(log/warn "ECHO token not provided for protected resource")
159+
(log/warn "Valid token not provided for protected resource")
160160
(response/not-allowed errors/token-required))))
161161

162162
(defn check-route-access

other/cmr-exchange/ous-plugin/resources/config/cmr-plugin/config.edn

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
{:api-version "v3"
22
:default-content-type "json"
3-
:auth-caching {:init {}
3+
:auth-caching {:jwt-public-key "{
4+
\"kty\": \"RSA\",
5+
\"n\": \"3LopSyeoSZZGorSPjk4mMbR0ybVSLvfrONGSGXCNXE6ScX9Y1QC_zV8fVeh4XO8tYDi9CgzqK3Nhjsd5KI0ZzTI8Lf52tyr7OzebZXGZpMvyvdp59wlbPL4WFkIHvWFpgypSrTQIRENKaYW_yQB9srq6JpUx14aRG5TpiBuPqgnGM-qBqPvLq5LX9kVhqbV46TuZd9uPn_gISut7A7K3Y5S24DZd3ebxXPap1cn6-mIY30QG5oYmVlMZxdVPhnTzjj4ZNsfyKSRKq3F_UapEr4ynhr-ONgj8HyozyFqTpUn3o8pKAMVaOEfZmRlqb3jnQknbcsJ9fNxmWUUZr_PysQ\",
6+
\"e\": \"AQAB\",
7+
\"kid\": \"edljwtpubkey_sit\"
8+
}"
9+
:init {}
410
:ttl {:minutes 60}
511
:lru {:threshold 1000}
612
:dumpfile "data/cache/authz-dump.edn"}
@@ -13,6 +19,9 @@
1319
:relative {:root {:url "/access-control"}}}}
1420
:concept {
1521
:variable {:version "1.1"}}
22+
:edl {:rest
23+
{:protocol "https"
24+
:host "sit.urs.earthdata.nasa.gov"}}
1625
:echo {:rest {:protocol "https"
1726
:host "cmr.sit.earthdata.nasa.gov"
1827
:context "/legacy-services/rest"}}

other/cmr-exchange/ous-plugin/src/cmr/ous/results/errors.clj

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
;; Authorization
1313

1414
(def no-permissions "You do not have permissions to access that resource.")
15-
(def token-required "An ECHO token is required to access this resource.")
15+
(def token-required "A valid token is required to access this resource.")
1616

1717
;; OUS - General
1818

other/cmr-exchange/service-bridge/resources/config/cmr-opendap/config.edn

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
{:api-version "v3"
22
:default-content-type "json"
3-
:auth-caching {:init {}
3+
:auth-caching {:jwt-public-key "{
4+
\"kty\": \"RSA\",
5+
\"n\": \"3LopSyeoSZZGorSPjk4mMbR0ybVSLvfrONGSGXCNXE6ScX9Y1QC_zV8fVeh4XO8tYDi9CgzqK3Nhjsd5KI0ZzTI8Lf52tyr7OzebZXGZpMvyvdp59wlbPL4WFkIHvWFpgypSrTQIRENKaYW_yQB9srq6JpUx14aRG5TpiBuPqgnGM-qBqPvLq5LX9kVhqbV46TuZd9uPn_gISut7A7K3Y5S24DZd3ebxXPap1cn6-mIY30QG5oYmVlMZxdVPhnTzjj4ZNsfyKSRKq3F_UapEr4ynhr-ONgj8HyozyFqTpUn3o8pKAMVaOEfZmRlqb3jnQknbcsJ9fNxmWUUZr_PysQ\",
6+
\"e\": \"AQAB\",
7+
\"kid\": \"edljwtpubkey_sit\"
8+
}"
9+
:init {}
410
:ttl {:minutes 60}
511
:lru {:threshold 1000}
612
:dumpfile "data/cache/authz-dump.edn"}
@@ -13,6 +19,9 @@
1319
:relative {:root {:url "/access-control"}}}}
1420
:concept {
1521
:variable {:version "1.1"}}
22+
:edl {:rest
23+
{:protocol "https"
24+
:host "sit.urs.earthdata.nasa.gov"}}
1625
:echo {:rest {:protocol "https"
1726
:host "cmr.sit.earthdata.nasa.gov"
1827
:context "/legacy-services/rest"}}

other/cmr-exchange/service-bridge/test/cmr/opendap/tests/integration/app/admin.clj

+8-8
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,16 @@
3636
(let [response @(httpc/get (format "http://localhost:%s/service-bridge/ping"
3737
(test-system/http-port)))]
3838
(is (= 403 (:status response)))
39-
(is (= {:errors ["An ECHO token is required to access this resource."]}
39+
(is (= {:errors ["A valid token is required to access this resource."]}
4040
(response/parse-json-result (:body response))))))
4141
(testing "v2 routes that don't exist in v1 ..."
4242
(let [response @(httpc/get (format "http://localhost:%s/service-bridge/cache"
4343
(test-system/http-port))
44-
(-> {}
45-
(request/add-token-header
46-
(util/get-sit-token))
47-
(request/add-accept
48-
"application/vnd.cmr-service-bridge.v1+json")))]
44+
(-> {}
45+
(request/add-token-header
46+
(util/get-sit-token))
47+
(request/add-accept
48+
"application/vnd.cmr-service-bridge.v1+json")))]
4949
(is (= 404 (:status response)))))
5050
(testing "v2 routes ..."
5151
#_(let [response @(httpc/get (format "http://localhost:%s/service-bridge/cache/auth"
@@ -55,7 +55,7 @@
5555
(util/get-sit-token))
5656
(request/add-accept
5757
"application/vnd.cmr-service-bridge.v2+json")))]
58-
(is (= 200 (:status response))))
58+
(is (= 200 (:status response))))
5959
;; XXX This test is currently failing; this has happened before with soft references
6060
;; see the code comment above cmr.http.kit.response/soft-reference->json! ...
6161
#_(let [response @(httpc/get (format "http://localhost:%s/service-bridge/cache/concept"
@@ -65,7 +65,7 @@
6565
(util/get-sit-token))
6666
(request/add-accept
6767
"application/vnd.cmr-service-bridge.v2+json")))]
68-
(is (= 200 (:status response))))))
68+
(is (= 200 (:status response))))))
6969

7070
(deftest testing-routes
7171
(is 401

other/cmr-exchange/service-bridge/test/cmr/opendap/tests/integration/app/ous.clj

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,5 @@
7070
(test-system/http-port)
7171
collection-id))]
7272
(is (= 403 (:status response)))
73-
(is (= {:errors ["An ECHO token is required to access this resource."]}
73+
(is (= {:errors ["A valid token is required to access this resource."]}
7474
(response/parse-json-result (:body response)))))))

0 commit comments

Comments
 (0)