|
1 | 1 | (ns cmr.common-app.api.launchpad-token-validation |
2 | 2 | "Validate Launchpad token." |
3 | 3 | (:require |
| 4 | + [cmr.acl.core :as acl] |
4 | 5 | [cmr.common-app.config :as config] |
| 6 | + [cmr.common.log :refer [info]] |
5 | 7 | [cmr.common.services.errors :as errors] |
6 | 8 | [cmr.common.util :as common-util] |
7 | | - [cmr.transmit.config :as transmit-config])) |
| 9 | + [cmr.transmit.config :as transmit-config] |
| 10 | + [cmr.transmit.tokens :as tokens])) |
8 | 11 |
|
9 | 12 | ;; TODO - remove legacy token check after legacy token retirement |
10 | 13 | (defn get-token-type |
|
15 | 18 | (= (transmit-config/echo-system-token) token) "System" |
16 | 19 | (re-seq #"[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}" token) "Echo-Token" |
17 | 20 | (common-util/is-legacy-token? token) "Legacy-EDL" |
| 21 | + (tokens/is-launchpad-federated-jwt? token) "JWT-Launchpad-Level-5" |
| 22 | + (tokens/is-edl-mfa-jwt? token) "JWT-EDL-MFA-Level-4" |
18 | 23 | (common-util/is-jwt-token? token) "JWT" |
19 | 24 | :else "Launchpad"))) |
20 | 25 |
|
21 | | -(defn validate-launchpad-token |
22 | | - "Validate the token in request context is a Launchpad Token if launchpad token enforcement |
23 | | - is turned on. This function should be called on routes that will ingest into CMR. |
24 | | - Ingest will only be allowed if the user is in the NAMS CMR Ingest group |
25 | | - and also has the right ACLs which is based on Earthdata Login uid." |
26 | | - [request-context] |
27 | | - (let [token (:token request-context)] |
28 | | - (when (and (config/launchpad-token-enforced) |
29 | | - (not (common-util/is-launchpad-token? token)) |
30 | | - (not= (transmit-config/echo-system-token) token)) |
31 | | - (errors/throw-service-error |
32 | | - :bad-request |
33 | | - (format "Launchpad token is required. Token [%s] is not a launchpad token." (common-util/scrub-token token)))))) |
| 26 | +(defn- get-token-info |
| 27 | + "Returns token validation info. Returns map with :type, :valid?, :assurance-level, :requires-draft-acl?" |
| 28 | + [token] |
| 29 | + (let [is-jwt? (common-util/is-jwt-token? token) |
| 30 | + assurance-level (when is-jwt? (tokens/get-assurance-level token)) |
| 31 | + is-saml? (common-util/is-launchpad-token? token) |
| 32 | + |
| 33 | + valid-saml? (and is-saml? (config/enable-launchpad-saml-authentication)) |
| 34 | + valid-jwt? (and is-jwt? |
| 35 | + (config/enable-idfed-jwt-authentication) |
| 36 | + assurance-level |
| 37 | + (>= assurance-level (config/required-assurance-level)))] |
| 38 | + {:type (cond |
| 39 | + (= token (transmit-config/echo-system-token)) :system |
| 40 | + is-saml? :saml |
| 41 | + (and is-jwt? (= assurance-level 5)) :jwt-level-5 |
| 42 | + (and is-jwt? (= assurance-level 4)) :jwt-level-4 |
| 43 | + is-jwt? :jwt-other |
| 44 | + :else :unknown) |
| 45 | + :valid? (or valid-saml? valid-jwt?) |
| 46 | + :assurance-level assurance-level |
| 47 | + :requires-draft-acl? (and is-jwt? (= assurance-level 4))})) |
| 48 | + |
| 49 | +(defn- build-error-message |
| 50 | + "Builds error message for invalid token." |
| 51 | + [token token-info] |
| 52 | + (let [{:keys [type assurance-level]} token-info |
| 53 | + saml-enabled? (config/enable-launchpad-saml-authentication) |
| 54 | + jwt-enabled? (config/enable-idfed-jwt-authentication) |
| 55 | + required-level (config/required-assurance-level)] |
| 56 | + (cond |
| 57 | + (not (or saml-enabled? jwt-enabled?)) |
| 58 | + "Write operations are disabled. Both Launchpad SAML and IDFed JWT authentication are turned off." |
| 59 | + |
| 60 | + (and (#{:jwt-level-4 :jwt-other} type) assurance-level jwt-enabled? (< assurance-level required-level)) |
| 61 | + (format "Token [%s] has assurance_level %d, but level %d or higher is required." |
| 62 | + (common-util/scrub-token token) assurance-level required-level) |
| 63 | + |
| 64 | + (and (#{:jwt-level-5 :jwt-level-4 :jwt-other} type) (not jwt-enabled?)) |
| 65 | + (format "Token [%s] is a JWT token, but IDFed JWT authentication is disabled." |
| 66 | + (common-util/scrub-token token)) |
| 67 | + |
| 68 | + (and (= type :saml) (not saml-enabled?)) |
| 69 | + (format "Token [%s] is a Launchpad SAML token, but SAML authentication is disabled." |
| 70 | + (common-util/scrub-token token)) |
| 71 | + |
| 72 | + :else |
| 73 | + (format "Token [%s] is not authorized for write operations." |
| 74 | + (common-util/scrub-token token))))) |
| 75 | + |
| 76 | +(defn validate-write-token |
| 77 | + "Validates token is authorized for write operations. Level 4 JWT tokens require NON_NASA_DRAFT_USER ACL." |
| 78 | + ([request-context] |
| 79 | + (validate-write-token request-context nil)) |
| 80 | + ([request-context provider-id] |
| 81 | + (let [token (:token request-context)] |
| 82 | + (when (and (config/launchpad-token-enforced) |
| 83 | + (not= token (transmit-config/echo-system-token))) |
| 84 | + |
| 85 | + (let [token-info (get-token-info token)] |
| 86 | + (info (format "Token validation - Type: %s, Valid: %s" (:type token-info) (:valid? token-info))) |
| 87 | + |
| 88 | + (when-not (:valid? token-info) |
| 89 | + (errors/throw-service-error :bad-request (build-error-message token token-info))) |
| 90 | + |
| 91 | + (when (and provider-id (:requires-draft-acl? token-info)) |
| 92 | + (acl/verify-non-nasa-draft-permission request-context :update :provider-object provider-id))))))) |
| 93 | + |
0 commit comments