Skip to content

Commit

Permalink
CORE-1112: modified terrain so that it uses a command-line option rat…
Browse files Browse the repository at this point in the history
…her than environment variables to enable fake authentication
  • Loading branch information
slr71 committed Jul 28, 2020
1 parent ca0bfb7 commit b662339
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 46 deletions.
45 changes: 28 additions & 17 deletions src/terrain/auth/user_attributes.clj
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
(ns terrain.auth.user-attributes
(:use [slingshot.slingshot :only [try+]])
(:require [clojure.string :as string]
[clojure.tools.logging :as log]
[clojure-commons.response :as resp]
[clojure-commons.exception :as cx]
[clojure-commons.exception-util :as cxu]
[terrain.clients.iplant-groups.subjects :as subjects]
[terrain.util.config :as cfg]
[terrain.util.jwt :as jwt]
[terrain.util.oauth :as oauth-util]
[terrain.util.keycloak-oidc :as keycloak-oidc-util]))

(def
^{:doc "The username to use when we're using fake authentication."
:dynamic true}
fake-user nil)

(def
^{:doc "The authenticated user or nil if the service is unsecured."
:dynamic true}
Expand Down Expand Up @@ -40,15 +48,26 @@
(jwt/terrain-user-from-jwt-claims jwt-claims jwt/user-from-wso2-assertion))

(defn fake-user-from-attributes
"Creates a real map of fake values for a user base on environment variables."
"Uses the username bound to `fake-user` to obtain user attributes. The subject lookup happens with every request
so that terrain doesn't have to be restarted if the subject lookup fails when terrain is starting up. This adds
a little bit of overhead to each request when fake authentication is enabled, but it can avoid problems if, for
example, iplant-groups isn't available when terrain is started."
[& _]
{:username (System/getenv "IPLANT_CAS_USER")
:password (System/getenv "IPLANT_CAS_PASS")
:email (System/getenv "IPLANT_CAS_EMAIL")
:shortUsername (System/getenv "IPLANT_CAS_SHORT")
:firstName (System/getenv "IPLANT_CAS_FIRST")
:lastName (System/getenv "IPLANT_CAS_LAST")
:commonName (System/getenv "IPLANT_CAS_COMMON")})
(if fake-user
(try+
(let [subject (subjects/lookup-subject (cfg/grouper-user) fake-user)]
{:username (str (:id subject) "@" (cfg/uid-domain))
:password nil
:email (:email subject)
:shortUsername (:id subject)
:firstName (:first_name subject)
:lastName (:last_name subject)
:commonName (:description subject)})
(catch [:status 404] _
(cxu/internal-system-error (str "fake user " fake-user " not found")))
(catch Object _
(cxu/internal-system-error (str "fake user lookup for username " fake-user " failed"))))
(cxu/internal-system-error (str "no fake user specified on command line"))))

(defn- user-info-from-current-user
"Converts the current-user to the user info structure expected in the request."
Expand Down Expand Up @@ -84,7 +103,7 @@
(defn- get-fake-auth
"Returns a non-nil value if we're using fake authentication."
[_]
(System/getenv "IPLANT_CAS_FAKE"))
fake-user)

(defn- get-de-jwt-assertion
"Extracts a JWT assertion from the request header used by the DE, returning nil if none is
Expand Down Expand Up @@ -188,14 +207,6 @@
(handler req)
(resp/unauthorized "No authentication information found in request."))))

(defn fake-store-current-user
"Fake storage of a user"
[handler & _]
(fn [req]
(log/info "Storing current user from IPLANT_CAS_* env vars.")
(binding [current-user (fake-user-from-attributes req)]
(handler req))))

(defmacro with-user
"Performs a task with the given user information bound to current-user. This macro is used
for debugging in the REPL."
Expand Down
23 changes: 10 additions & 13 deletions src/terrain/clients/iplant_groups.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
[clojure-commons.exception-util :as cxu]
[cyverse-groups-client.core :as c]
[terrain.clients.apps.raw :as apps-client]
[terrain.clients.iplant-groups.subjects :as subjects]
[terrain.clients.metadata.raw :as metadata-client]
[terrain.util.config :as config]))

Expand Down Expand Up @@ -57,25 +58,21 @@
:institution ""
:source_id ""})

(defn- lookup-subject-url
[short-username]
(str (curl/url (config/ipg-base) "subjects" short-username)))

(defn lookup-subject
"Uses iplant-groups's subject lookup by ID endpoint to retrieve user details."
[user short-username]
(try
(if-let [user-info (-> (http/get (lookup-subject-url short-username) {:query-params {:user user} :as :json})
(:body))]
user-info
(do (log/warn (str "no user info found for username '" short-username "'"))
nil))
(catch Exception e
(log/error e (str "username lookup for '" short-username "' failed"))
(try+
(subjects/lookup-subject user short-username)
(catch [:status 404] _
(log/warn (str "no user info found for username '" short-username "'"))
nil)
(catch Object _
(log/error (:throwable &throw-context) "user lookup for '" short-username "' failed")
nil)))

(defn lookup-subject-add-empty
"Uses iplant-groups's subject lookup by ID endpoint to retrieve user details, returning an empty user info block if nothing is found."
"Uses iplant-groups's subject lookup by ID endpoint to retrieve user details, returning an empty user info block if
nothing is found."
[user short-username]
(if-let [user-info (lookup-subject user short-username)]
user-info
Expand Down
18 changes: 18 additions & 0 deletions src/terrain/clients/iplant_groups/subjects.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
(ns terrain.clients.iplant-groups.subjects
"Functions to perform subject lookups using the iplant-groups service. This namespace was originally created to
avoid a cirucular dependency in terrain.auth.user-attributes when terrain.clients.iplant-groups was added as a
dependency to that namespace."
(:require [cemerick.url :as curl]
[clj-http.client :as http]
[terrain.util.config :as config]))

(defn- lookup-subject-url
[short-username]
(str (curl/url (config/ipg-base) "subjects" short-username)))

(defn lookup-subject
"Uses iplant-groups's subject lookup by ID endpoint to retrieve user details."
[user short-username]
(:body (http/get (lookup-subject-url short-username)
{:query-params {:user user}
:as :json})))
42 changes: 27 additions & 15 deletions src/terrain/core.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
(ns terrain.core
(:gen-class)
(:use [clojure.java.io :only [file resource]])
(:require [terrain.util.config :as config]
(:use [clojure.java.io :only [file resource]]
[terrain.middleware :only [wrap-fake-user]])
(:require [terrain.routes :as routes]
[terrain.util.config :as config]
[clojure.tools.nrepl.server :as nrepl]
[me.raynes.fs :as fs]
[clj-http.client :as http]
Expand Down Expand Up @@ -59,10 +61,9 @@

(defn cli-options
[]
[["-c" "--config PATH" "Path to the config file"
:default "/etc/iplant/de/terrain.properties"]
["-v" "--version" "Print out the version number."]
["-h" "--help"]])
(concat
(ccli/cli-options)
[["-u" "--fake-user USERNAME" "The username to use for fake authentication"]]))

(def svc-info
{:desc "DE service for business logic"
Expand All @@ -78,20 +79,31 @@
((eval 'terrain.routes/app-wrapper) req)))

(defn run-jetty
[]
[port fake-user]
(require 'terrain.routes 'ring.adapter.jetty)
(log/warn "Started listening on" (config/listen-port))
((eval 'ring.adapter.jetty/run-jetty) (eval 'terrain.routes/app-wrapper) {:port (config/listen-port)}))
(log/warn "Started listening on" port)
((eval 'ring.adapter.jetty/run-jetty)
(wrap-fake-user routes/app-wrapper fake-user)
{:port port}))

(defn- load-config
[config-path]
(let [config-path (or config-path "/etc/iplant/de/terrain.properties")]
(when-not (fs/exists? config-path)
(ccli/exit 1 (str "The config file does not exist.")))
(when-not (fs/readable? config-path)
(ccli/exit 1 "The config file is not readable."))
(config/load-config-from-file config-path)))

(defn- get-port
[{:keys [port]}]
(or port (config/listen-port)))

(defn -main
[& args]
(tc/with-logging-context svc-info
(let [{:keys [options]} (ccli/handle-args svc-info args cli-options)]
(when-not (fs/exists? (:config options))
(ccli/exit 1 (str "The config file does not exist.")))
(when-not (fs/readable? (:config options))
(ccli/exit 1 "The config file is not readable."))
(config/load-config-from-file (:config options))
(load-config (:config options))
(http/with-connection-pool {:timeout 5 :threads 10 :insecure? false :default-per-route 10}
(icat/configure-icat)
(run-jetty)))))
(run-jetty (get-port options) (:fake-user options))))))
13 changes: 12 additions & 1 deletion src/terrain/middleware.clj
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(ns terrain.middleware
(:require [clojure.string :as string]))
(:require [clojure.string :as string]
[terrain.auth.user-attributes :as user-attributes]))

(defn- add-context-path
"Adds a context path to the start of a URI path if it's not present."
Expand All @@ -25,3 +26,13 @@
(update-in [:params] dissoc query-param (keyword query-param))
(update-in [:query-params] dissoc query-param (keyword query-param))
handler)))))

(defn wrap-fake-user
"Middleware that configures fake authentication for development testing. If the fake user is falsey,
the handler function is not wrapped at all."
[handler fake-user]
(if fake-user
(fn [request]
(binding [user-attributes/fake-user fake-user]
(handler request)))
handler))

0 comments on commit b662339

Please sign in to comment.