mirror of
https://github.com/penpot/penpot.git
synced 2025-01-23 23:18:48 -05:00
♻️ Refactor token generation API
This commit is contained in:
parent
44f4d9c50c
commit
d6d9d25fce
16 changed files with 167 additions and 183 deletions
|
@ -15,9 +15,11 @@
|
||||||
[app.common.uri :as u]
|
[app.common.uri :as u]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
|
[app.http.client :as http]
|
||||||
[app.http.middleware :as hmw]
|
[app.http.middleware :as hmw]
|
||||||
[app.loggers.audit :as audit]
|
[app.loggers.audit :as audit]
|
||||||
[app.rpc.queries.profile :as profile]
|
[app.rpc.queries.profile :as profile]
|
||||||
|
[app.tokens :as tokens]
|
||||||
[app.util.json :as json]
|
[app.util.json :as json]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[app.worker :as wrk]
|
[app.worker :as wrk]
|
||||||
|
@ -47,7 +49,7 @@
|
||||||
(defn- discover-oidc-config
|
(defn- discover-oidc-config
|
||||||
[{:keys [http-client]} {:keys [base-uri] :as opts}]
|
[{:keys [http-client]} {:keys [base-uri] :as opts}]
|
||||||
(let [discovery-uri (u/join base-uri ".well-known/openid-configuration")
|
(let [discovery-uri (u/join base-uri ".well-known/openid-configuration")
|
||||||
response (ex/try (http-client {:method :get :uri (str discovery-uri)} {:sync? true}))]
|
response (ex/try (http/req! http-client {:method :get :uri (str discovery-uri)} {:sync? true}))]
|
||||||
(cond
|
(cond
|
||||||
(ex/exception? response)
|
(ex/exception? response)
|
||||||
(do
|
(do
|
||||||
|
@ -158,10 +160,10 @@
|
||||||
(defn- retrieve-github-email
|
(defn- retrieve-github-email
|
||||||
[{:keys [http-client]} tdata info]
|
[{:keys [http-client]} tdata info]
|
||||||
(or (some-> info :email p/resolved)
|
(or (some-> info :email p/resolved)
|
||||||
(-> (http-client {:uri "https://api.github.com/user/emails"
|
(-> (http/req! http-client {:uri "https://api.github.com/user/emails"
|
||||||
:headers {"Authorization" (dm/str (:type tdata) " " (:token tdata))}
|
:headers {"Authorization" (dm/str (:type tdata) " " (:token tdata))}
|
||||||
:timeout 6000
|
:timeout 6000
|
||||||
:method :get})
|
:method :get})
|
||||||
(p/then (fn [{:keys [status body] :as response}]
|
(p/then (fn [{:keys [status body] :as response}]
|
||||||
(when-not (s/int-in-range? 200 300 status)
|
(when-not (s/int-in-range? 200 300 status)
|
||||||
(ex/raise :type :internal
|
(ex/raise :type :internal
|
||||||
|
@ -278,7 +280,7 @@
|
||||||
:uri (:token-uri provider)
|
:uri (:token-uri provider)
|
||||||
:body (u/map->query-string params)}]
|
:body (u/map->query-string params)}]
|
||||||
(p/then
|
(p/then
|
||||||
(http-client req)
|
(http/req! http-client req)
|
||||||
(fn [{:keys [status body] :as res}]
|
(fn [{:keys [status body] :as res}]
|
||||||
(if (= status 200)
|
(if (= status 200)
|
||||||
(let [data (json/read body)]
|
(let [data (json/read body)]
|
||||||
|
@ -292,11 +294,10 @@
|
||||||
(defn- retrieve-user-info
|
(defn- retrieve-user-info
|
||||||
[{:keys [provider http-client] :as cfg} tdata]
|
[{:keys [provider http-client] :as cfg} tdata]
|
||||||
(letfn [(retrieve []
|
(letfn [(retrieve []
|
||||||
(http-client {:uri (:user-uri provider)
|
(http/req! http-client {:uri (:user-uri provider)
|
||||||
:headers {"Authorization" (str (:type tdata) " " (:token tdata))}
|
:headers {"Authorization" (str (:type tdata) " " (:token tdata))}
|
||||||
:timeout 6000
|
:timeout 6000
|
||||||
:method :get}))
|
:method :get}))
|
||||||
|
|
||||||
(validate-response [response]
|
(validate-response [response]
|
||||||
(when-not (s/int-in-range? 200 300 (:status response))
|
(when-not (s/int-in-range? 200 300 (:status response))
|
||||||
(ex/raise :type :internal
|
(ex/raise :type :internal
|
||||||
|
@ -353,7 +354,7 @@
|
||||||
::props]))
|
::props]))
|
||||||
|
|
||||||
(defn retrieve-info
|
(defn retrieve-info
|
||||||
[{:keys [tokens provider] :as cfg} {:keys [params] :as request}]
|
[{:keys [sprops provider] :as cfg} {:keys [params] :as request}]
|
||||||
(letfn [(validate-oidc [info]
|
(letfn [(validate-oidc [info]
|
||||||
;; If the provider is OIDC, we can proceed to check
|
;; If the provider is OIDC, we can proceed to check
|
||||||
;; roles if they are defined.
|
;; roles if they are defined.
|
||||||
|
@ -392,7 +393,7 @@
|
||||||
|
|
||||||
(let [state (get params :state)
|
(let [state (get params :state)
|
||||||
code (get params :code)
|
code (get params :code)
|
||||||
state (tokens :verify {:token state :iss :oauth})]
|
state (tokens/verify sprops {:token state :iss :oauth})]
|
||||||
(-> (p/resolved code)
|
(-> (p/resolved code)
|
||||||
(p/then #(retrieve-access-token cfg %))
|
(p/then #(retrieve-access-token cfg %))
|
||||||
(p/then #(retrieve-user-info cfg %))
|
(p/then #(retrieve-user-info cfg %))
|
||||||
|
@ -420,13 +421,13 @@
|
||||||
(redirect-response uri)))
|
(redirect-response uri)))
|
||||||
|
|
||||||
(defn- generate-redirect
|
(defn- generate-redirect
|
||||||
[{:keys [tokens session audit] :as cfg} request info profile]
|
[{:keys [sprops session audit] :as cfg} request info profile]
|
||||||
(if profile
|
(if profile
|
||||||
(let [sxf ((:create session) (:id profile))
|
(let [sxf ((:create session) (:id profile))
|
||||||
token (or (:invitation-token info)
|
token (or (:invitation-token info)
|
||||||
(tokens :generate {:iss :auth
|
(tokens/generate sprops {:iss :auth
|
||||||
:exp (dt/in-future "15m")
|
:exp (dt/in-future "15m")
|
||||||
:profile-id (:id profile)}))
|
:profile-id (:id profile)}))
|
||||||
params {:token token}
|
params {:token token}
|
||||||
|
|
||||||
uri (-> (u/uri (:public-uri cfg))
|
uri (-> (u/uri (:public-uri cfg))
|
||||||
|
@ -448,7 +449,7 @@
|
||||||
:iss :prepared-register
|
:iss :prepared-register
|
||||||
:is-active true
|
:is-active true
|
||||||
:exp (dt/in-future {:hours 48}))
|
:exp (dt/in-future {:hours 48}))
|
||||||
token (tokens :generate info)
|
token (tokens/generate sprops info)
|
||||||
params (d/without-nils
|
params (d/without-nils
|
||||||
{:token token
|
{:token token
|
||||||
:fullname (:fullname info)})
|
:fullname (:fullname info)})
|
||||||
|
@ -458,13 +459,13 @@
|
||||||
(redirect-response uri))))
|
(redirect-response uri))))
|
||||||
|
|
||||||
(defn- auth-handler
|
(defn- auth-handler
|
||||||
[{:keys [tokens] :as cfg} {:keys [params] :as request}]
|
[{:keys [sprops] :as cfg} {:keys [params] :as request}]
|
||||||
(let [props (audit/extract-utm-params params)
|
(let [props (audit/extract-utm-params params)
|
||||||
state (tokens :generate
|
state (tokens/generate sprops
|
||||||
{:iss :oauth
|
{:iss :oauth
|
||||||
:invitation-token (:invitation-token params)
|
:invitation-token (:invitation-token params)
|
||||||
:props props
|
:props props
|
||||||
:exp (dt/in-future "15m")})
|
:exp (dt/in-future "15m")})
|
||||||
uri (build-auth-uri cfg state)]
|
uri (build-auth-uri cfg state)]
|
||||||
(yrs/response 200 {:redirect-uri uri})))
|
(yrs/response 200 {:redirect-uri uri})))
|
||||||
|
|
||||||
|
@ -496,16 +497,16 @@
|
||||||
:hint "provider not configured"))))))})
|
:hint "provider not configured"))))))})
|
||||||
|
|
||||||
(s/def ::public-uri ::us/not-empty-string)
|
(s/def ::public-uri ::us/not-empty-string)
|
||||||
(s/def ::http-client fn?)
|
(s/def ::http-client ::http/client)
|
||||||
(s/def ::session map?)
|
(s/def ::session map?)
|
||||||
(s/def ::tokens fn?)
|
(s/def ::sprops map?)
|
||||||
(s/def ::providers map?)
|
(s/def ::providers map?)
|
||||||
|
|
||||||
(defmethod ig/pre-init-spec ::routes
|
(defmethod ig/pre-init-spec ::routes
|
||||||
[_]
|
[_]
|
||||||
(s/keys :req-un [::public-uri
|
(s/keys :req-un [::public-uri
|
||||||
::session
|
::session
|
||||||
::tokens
|
::sprops
|
||||||
::http-client
|
::http-client
|
||||||
::providers
|
::providers
|
||||||
::db/pool
|
::db/pool
|
||||||
|
|
|
@ -114,18 +114,18 @@
|
||||||
;; HTTP ROUTER
|
;; HTTP ROUTER
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(s/def ::oauth map?)
|
|
||||||
(s/def ::storage map?)
|
|
||||||
(s/def ::assets map?)
|
(s/def ::assets map?)
|
||||||
(s/def ::feedback fn?)
|
|
||||||
(s/def ::ws fn?)
|
|
||||||
(s/def ::audit-handler fn?)
|
(s/def ::audit-handler fn?)
|
||||||
(s/def ::awsns-handler fn?)
|
(s/def ::awsns-handler fn?)
|
||||||
(s/def ::session map?)
|
|
||||||
(s/def ::rpc-routes (s/nilable vector?))
|
|
||||||
(s/def ::debug-routes (s/nilable vector?))
|
(s/def ::debug-routes (s/nilable vector?))
|
||||||
(s/def ::oidc-routes (s/nilable vector?))
|
|
||||||
(s/def ::doc-routes (s/nilable vector?))
|
(s/def ::doc-routes (s/nilable vector?))
|
||||||
|
(s/def ::feedback fn?)
|
||||||
|
(s/def ::oauth map?)
|
||||||
|
(s/def ::oidc-routes (s/nilable vector?))
|
||||||
|
(s/def ::rpc-routes (s/nilable vector?))
|
||||||
|
(s/def ::session map?)
|
||||||
|
(s/def ::storage map?)
|
||||||
|
(s/def ::ws fn?)
|
||||||
|
|
||||||
(defmethod ig/pre-init-spec ::router [_]
|
(defmethod ig/pre-init-spec ::router [_]
|
||||||
(s/keys :req-un [::mtx/metrics
|
(s/keys :req-un [::mtx/metrics
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.db.sql :as sql]
|
[app.db.sql :as sql]
|
||||||
|
[app.http.client :as http]
|
||||||
|
[app.tokens :as tokens]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[integrant.core :as ig]
|
[integrant.core :as ig]
|
||||||
|
@ -24,10 +26,11 @@
|
||||||
(declare parse-notification)
|
(declare parse-notification)
|
||||||
(declare process-report)
|
(declare process-report)
|
||||||
|
|
||||||
(s/def ::http-client fn?)
|
(s/def ::http-client ::http/client)
|
||||||
|
(s/def ::sprops map?)
|
||||||
|
|
||||||
(defmethod ig/pre-init-spec ::handler [_]
|
(defmethod ig/pre-init-spec ::handler [_]
|
||||||
(s/keys :req-un [::db/pool ::http-client]))
|
(s/keys :req-un [::db/pool ::http-client ::sprops]))
|
||||||
|
|
||||||
(defmethod ig/init-key ::handler
|
(defmethod ig/init-key ::handler
|
||||||
[_ {:keys [executor] :as cfg}]
|
[_ {:keys [executor] :as cfg}]
|
||||||
|
@ -46,7 +49,7 @@
|
||||||
(let [surl (get body "SubscribeURL")
|
(let [surl (get body "SubscribeURL")
|
||||||
stopic (get body "TopicArn")]
|
stopic (get body "TopicArn")]
|
||||||
(l/info :action "subscription received" :topic stopic :url surl)
|
(l/info :action "subscription received" :topic stopic :url surl)
|
||||||
(http-client {:uri surl :method :post :timeout 10000} {:sync? true}))
|
(http/req! http-client {:uri surl :method :post :timeout 10000} {:sync? true}))
|
||||||
|
|
||||||
(= mtype "Notification")
|
(= mtype "Notification")
|
||||||
(when-let [message (parse-json (get body "Message"))]
|
(when-let [message (parse-json (get body "Message"))]
|
||||||
|
@ -97,10 +100,10 @@
|
||||||
(get mail "headers")))
|
(get mail "headers")))
|
||||||
|
|
||||||
(defn- extract-identity
|
(defn- extract-identity
|
||||||
[{:keys [tokens] :as cfg} headers]
|
[{:keys [sprops]} headers]
|
||||||
(let [tdata (get headers "x-penpot-data")]
|
(let [tdata (get headers "x-penpot-data")]
|
||||||
(when-not (str/empty? tdata)
|
(when-not (str/empty? tdata)
|
||||||
(let [result (tokens :verify {:token tdata :iss :profile-identity})]
|
(let [result (tokens/verify sprops {:token tdata :iss :profile-identity})]
|
||||||
(:profile-id result)))))
|
(:profile-id result)))))
|
||||||
|
|
||||||
(defn- parse-notification
|
(defn- parse-notification
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
(http/send-async req {:client client :as response-type}))))
|
(http/send-async req {:client client :as response-type}))))
|
||||||
{::client client})))
|
{::client client})))
|
||||||
|
|
||||||
|
|
||||||
(defn req!
|
(defn req!
|
||||||
"A convencience toplevel function for gradual migration to a new API
|
"A convencience toplevel function for gradual migration to a new API
|
||||||
convention."
|
convention."
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.db.sql :as sql]
|
[app.db.sql :as sql]
|
||||||
|
[app.tokens :as tokens]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[app.worker :as wrk]
|
[app.worker :as wrk]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
|
@ -39,7 +40,7 @@
|
||||||
(delete-session [store key]))
|
(delete-session [store key]))
|
||||||
|
|
||||||
(defn- make-database-store
|
(defn- make-database-store
|
||||||
[{:keys [pool tokens executor]}]
|
[{:keys [pool sprops executor]}]
|
||||||
(reify ISessionStore
|
(reify ISessionStore
|
||||||
(read-session [_ token]
|
(read-session [_ token]
|
||||||
(px/with-dispatch executor
|
(px/with-dispatch executor
|
||||||
|
@ -50,9 +51,9 @@
|
||||||
(let [profile-id (:profile-id data)
|
(let [profile-id (:profile-id data)
|
||||||
user-agent (:user-agent data)
|
user-agent (:user-agent data)
|
||||||
created-at (or (:created-at data) (dt/now))
|
created-at (or (:created-at data) (dt/now))
|
||||||
token (tokens :generate {:iss "authentication"
|
token (tokens/generate sprops {:iss "authentication"
|
||||||
:iat created-at
|
:iat created-at
|
||||||
:uid profile-id})
|
:uid profile-id})
|
||||||
params {:user-agent user-agent
|
params {:user-agent user-agent
|
||||||
:profile-id profile-id
|
:profile-id profile-id
|
||||||
:created-at created-at
|
:created-at created-at
|
||||||
|
@ -75,7 +76,7 @@
|
||||||
nil))))
|
nil))))
|
||||||
|
|
||||||
(defn make-inmemory-store
|
(defn make-inmemory-store
|
||||||
[{:keys [tokens]}]
|
[{:keys [sprops]}]
|
||||||
(let [cache (atom {})]
|
(let [cache (atom {})]
|
||||||
(reify ISessionStore
|
(reify ISessionStore
|
||||||
(read-session [_ token]
|
(read-session [_ token]
|
||||||
|
@ -86,9 +87,9 @@
|
||||||
(let [profile-id (:profile-id data)
|
(let [profile-id (:profile-id data)
|
||||||
user-agent (:user-agent data)
|
user-agent (:user-agent data)
|
||||||
created-at (or (:created-at data) (dt/now))
|
created-at (or (:created-at data) (dt/now))
|
||||||
token (tokens :generate {:iss "authentication"
|
token (tokens/generate sprops {:iss "authentication"
|
||||||
:iat created-at
|
:iat created-at
|
||||||
:uid profile-id})
|
:uid profile-id})
|
||||||
params {:user-agent user-agent
|
params {:user-agent user-agent
|
||||||
:created-at created-at
|
:created-at created-at
|
||||||
:updated-at created-at
|
:updated-at created-at
|
||||||
|
@ -108,9 +109,9 @@
|
||||||
(swap! cache dissoc token)
|
(swap! cache dissoc token)
|
||||||
nil)))))
|
nil)))))
|
||||||
|
|
||||||
(s/def ::tokens fn?)
|
(s/def ::sprops map?)
|
||||||
(defmethod ig/pre-init-spec ::store [_]
|
(defmethod ig/pre-init-spec ::store [_]
|
||||||
(s/keys :req-un [::db/pool ::wrk/executor ::tokens]))
|
(s/keys :req-un [::db/pool ::wrk/executor ::sprops]))
|
||||||
|
|
||||||
(defmethod ig/init-key ::store
|
(defmethod ig/init-key ::store
|
||||||
[_ {:keys [pool] :as cfg}]
|
[_ {:keys [pool] :as cfg}]
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
|
[app.tokens :as tokens]
|
||||||
[app.util.async :as aa]
|
[app.util.async :as aa]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[app.worker :as wrk]
|
[app.worker :as wrk]
|
||||||
|
@ -237,10 +238,10 @@
|
||||||
|
|
||||||
(s/def ::http-client fn?)
|
(s/def ::http-client fn?)
|
||||||
(s/def ::uri ::us/string)
|
(s/def ::uri ::us/string)
|
||||||
(s/def ::tokens fn?)
|
(s/def ::sprops map?)
|
||||||
|
|
||||||
(defmethod ig/pre-init-spec ::archive-task [_]
|
(defmethod ig/pre-init-spec ::archive-task [_]
|
||||||
(s/keys :req-un [::db/pool ::tokens ::http-client]
|
(s/keys :req-un [::db/pool ::sprops ::http-client]
|
||||||
:opt-un [::uri]))
|
:opt-un [::uri]))
|
||||||
|
|
||||||
(defmethod ig/init-key ::archive-task
|
(defmethod ig/init-key ::archive-task
|
||||||
|
@ -276,7 +277,7 @@
|
||||||
for update skip locked;")
|
for update skip locked;")
|
||||||
|
|
||||||
(defn archive-events
|
(defn archive-events
|
||||||
[{:keys [pool uri tokens http-client] :as cfg}]
|
[{:keys [pool uri sprops http-client] :as cfg}]
|
||||||
(letfn [(decode-row [{:keys [props ip-addr context] :as row}]
|
(letfn [(decode-row [{:keys [props ip-addr context] :as row}]
|
||||||
(cond-> row
|
(cond-> row
|
||||||
(db/pgobject? props)
|
(db/pgobject? props)
|
||||||
|
@ -300,9 +301,9 @@
|
||||||
:context]))
|
:context]))
|
||||||
|
|
||||||
(send [events]
|
(send [events]
|
||||||
(let [token (tokens :generate {:iss "authentication"
|
(let [token (tokens/generate sprops {:iss "authentication"
|
||||||
:iat (dt/now)
|
:iat (dt/now)
|
||||||
:uid uuid/zero})
|
:uid uuid/zero})
|
||||||
body (t/encode {:events events})
|
body (t/encode {:events events})
|
||||||
headers {"content-type" "application/transit+json"
|
headers {"content-type" "application/transit+json"
|
||||||
"origin" (cf/get :public-uri)
|
"origin" (cf/get :public-uri)
|
||||||
|
|
|
@ -69,9 +69,6 @@
|
||||||
:executor (ig/ref [::default :app.worker/executor])
|
:executor (ig/ref [::default :app.worker/executor])
|
||||||
:redis-uri (cf/get :redis-uri)}
|
:redis-uri (cf/get :redis-uri)}
|
||||||
|
|
||||||
:app.tokens/tokens
|
|
||||||
{:keys (ig/ref :app.setup/keys)}
|
|
||||||
|
|
||||||
:app.storage.tmp/cleaner
|
:app.storage.tmp/cleaner
|
||||||
{:executor (ig/ref [::worker :app.worker/executor])
|
{:executor (ig/ref [::worker :app.worker/executor])
|
||||||
:scheduler (ig/ref :app.worker/scheduler)}
|
:scheduler (ig/ref :app.worker/scheduler)}
|
||||||
|
@ -92,7 +89,7 @@
|
||||||
|
|
||||||
:app.http.session/store
|
:app.http.session/store
|
||||||
{:pool (ig/ref :app.db/pool)
|
{:pool (ig/ref :app.db/pool)
|
||||||
:tokens (ig/ref :app.tokens/tokens)
|
:sprops (ig/ref :app.setup/props)
|
||||||
:executor (ig/ref [::default :app.worker/executor])}
|
:executor (ig/ref [::default :app.worker/executor])}
|
||||||
|
|
||||||
:app.http.session/gc-task
|
:app.http.session/gc-task
|
||||||
|
@ -100,7 +97,7 @@
|
||||||
:max-age (cf/get :auth-token-cookie-max-age)}
|
:max-age (cf/get :auth-token-cookie-max-age)}
|
||||||
|
|
||||||
:app.http.awsns/handler
|
:app.http.awsns/handler
|
||||||
{:tokens (ig/ref :app.tokens/tokens)
|
{:sprops (ig/ref :app.setup/props)
|
||||||
:pool (ig/ref :app.db/pool)
|
:pool (ig/ref :app.db/pool)
|
||||||
:http-client (ig/ref :app.http/client)
|
:http-client (ig/ref :app.http/client)
|
||||||
:executor (ig/ref [::worker :app.worker/executor])}
|
:executor (ig/ref [::worker :app.worker/executor])}
|
||||||
|
@ -168,13 +165,14 @@
|
||||||
:github (ig/ref :app.auth.oidc/github-provider)
|
:github (ig/ref :app.auth.oidc/github-provider)
|
||||||
:gitlab (ig/ref :app.auth.oidc/gitlab-provider)
|
:gitlab (ig/ref :app.auth.oidc/gitlab-provider)
|
||||||
:oidc (ig/ref :app.auth.oidc/generic-provider)}
|
:oidc (ig/ref :app.auth.oidc/generic-provider)}
|
||||||
:tokens (ig/ref :app.tokens/tokens)
|
:sprops (ig/ref :app.setup/props)
|
||||||
:http-client (ig/ref :app.http/client)
|
:http-client (ig/ref :app.http/client)
|
||||||
:pool (ig/ref :app.db/pool)
|
:pool (ig/ref :app.db/pool)
|
||||||
:session (ig/ref :app.http/session)
|
:session (ig/ref :app.http/session)
|
||||||
:public-uri (cf/get :public-uri)
|
:public-uri (cf/get :public-uri)
|
||||||
:executor (ig/ref [::default :app.worker/executor])}
|
:executor (ig/ref [::default :app.worker/executor])}
|
||||||
|
|
||||||
|
;; TODO: revisit the dependencies of this service, looks they are too much unused of them
|
||||||
:app.http/router
|
:app.http/router
|
||||||
{:assets (ig/ref :app.http.assets/handlers)
|
{:assets (ig/ref :app.http.assets/handlers)
|
||||||
:feedback (ig/ref :app.http.feedback/handler)
|
:feedback (ig/ref :app.http.feedback/handler)
|
||||||
|
@ -186,7 +184,6 @@
|
||||||
:metrics (ig/ref :app.metrics/metrics)
|
:metrics (ig/ref :app.metrics/metrics)
|
||||||
:public-uri (cf/get :public-uri)
|
:public-uri (cf/get :public-uri)
|
||||||
:storage (ig/ref :app.storage/storage)
|
:storage (ig/ref :app.storage/storage)
|
||||||
:tokens (ig/ref :app.tokens/tokens)
|
|
||||||
:audit-handler (ig/ref :app.loggers.audit/http-handler)
|
:audit-handler (ig/ref :app.loggers.audit/http-handler)
|
||||||
:rpc-routes (ig/ref :app.rpc/routes)
|
:rpc-routes (ig/ref :app.rpc/routes)
|
||||||
:doc-routes (ig/ref :app.rpc.doc/routes)
|
:doc-routes (ig/ref :app.rpc.doc/routes)
|
||||||
|
@ -218,7 +215,7 @@
|
||||||
:app.rpc/methods
|
:app.rpc/methods
|
||||||
{:pool (ig/ref :app.db/pool)
|
{:pool (ig/ref :app.db/pool)
|
||||||
:session (ig/ref :app.http/session)
|
:session (ig/ref :app.http/session)
|
||||||
:tokens (ig/ref :app.tokens/tokens)
|
:sprops (ig/ref :app.setup/props)
|
||||||
:metrics (ig/ref :app.metrics/metrics)
|
:metrics (ig/ref :app.metrics/metrics)
|
||||||
:storage (ig/ref :app.storage/storage)
|
:storage (ig/ref :app.storage/storage)
|
||||||
:msgbus (ig/ref :app.msgbus/msgbus)
|
:msgbus (ig/ref :app.msgbus/msgbus)
|
||||||
|
@ -293,8 +290,8 @@
|
||||||
{:pool (ig/ref :app.db/pool)
|
{:pool (ig/ref :app.db/pool)
|
||||||
:key (cf/get :secret-key)}
|
:key (cf/get :secret-key)}
|
||||||
|
|
||||||
:app.setup/keys
|
;; :app.setup/keys
|
||||||
{:props (ig/ref :app.setup/props)}
|
;; {:props (ig/ref :app.setup/props)}
|
||||||
|
|
||||||
:app.loggers.zmq/receiver
|
:app.loggers.zmq/receiver
|
||||||
{:endpoint (cf/get :loggers-zmq-uri)}
|
{:endpoint (cf/get :loggers-zmq-uri)}
|
||||||
|
@ -309,7 +306,7 @@
|
||||||
|
|
||||||
:app.loggers.audit/archive-task
|
:app.loggers.audit/archive-task
|
||||||
{:uri (cf/get :audit-log-archive-uri)
|
{:uri (cf/get :audit-log-archive-uri)
|
||||||
:tokens (ig/ref :app.tokens/tokens)
|
:sprops (ig/ref :app.setup/props)
|
||||||
:pool (ig/ref :app.db/pool)
|
:pool (ig/ref :app.db/pool)
|
||||||
:http-client (ig/ref :app.http/client)}
|
:http-client (ig/ref :app.http/client)}
|
||||||
|
|
||||||
|
|
|
@ -258,12 +258,12 @@
|
||||||
(s/def ::public-uri ::us/not-empty-string)
|
(s/def ::public-uri ::us/not-empty-string)
|
||||||
(s/def ::session map?)
|
(s/def ::session map?)
|
||||||
(s/def ::storage some?)
|
(s/def ::storage some?)
|
||||||
(s/def ::tokens fn?)
|
(s/def ::sprops map?)
|
||||||
|
|
||||||
(defmethod ig/pre-init-spec ::methods [_]
|
(defmethod ig/pre-init-spec ::methods [_]
|
||||||
(s/keys :req-un [::storage
|
(s/keys :req-un [::storage
|
||||||
::session
|
::session
|
||||||
::tokens
|
::sprops
|
||||||
::audit
|
::audit
|
||||||
::executors
|
::executors
|
||||||
::public-uri
|
::public-uri
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
[app.rpc.mutations.teams :as teams]
|
[app.rpc.mutations.teams :as teams]
|
||||||
[app.rpc.queries.profile :as profile]
|
[app.rpc.queries.profile :as profile]
|
||||||
[app.rpc.rlimit :as rlimit]
|
[app.rpc.rlimit :as rlimit]
|
||||||
|
[app.tokens :as tokens]
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[buddy.hashers :as hashers]
|
[buddy.hashers :as hashers]
|
||||||
|
@ -80,7 +81,7 @@
|
||||||
;; ---- COMMAND: login with password
|
;; ---- COMMAND: login with password
|
||||||
|
|
||||||
(defn login-with-password
|
(defn login-with-password
|
||||||
[{:keys [pool session tokens] :as cfg} {:keys [email password] :as params}]
|
[{:keys [pool session sprops] :as cfg} {:keys [email password] :as params}]
|
||||||
|
|
||||||
(when-not (contains? cf/flags :login)
|
(when-not (contains? cf/flags :login)
|
||||||
(ex/raise :type :restriction
|
(ex/raise :type :restriction
|
||||||
|
@ -114,7 +115,7 @@
|
||||||
(profile/decode-profile-row))
|
(profile/decode-profile-row))
|
||||||
|
|
||||||
invitation (when-let [token (:invitation-token params)]
|
invitation (when-let [token (:invitation-token params)]
|
||||||
(tokens :verify {:token token :iss :team-invitation}))
|
(tokens/verify sprops {:token token :iss :team-invitation}))
|
||||||
|
|
||||||
;; If invitation member-id does not matches the profile-id, we just proceed to ignore the
|
;; If invitation member-id does not matches the profile-id, we just proceed to ignore the
|
||||||
;; invitation because invitations matches exactly; and user can't loging with other email and
|
;; invitation because invitations matches exactly; and user can't loging with other email and
|
||||||
|
@ -156,9 +157,9 @@
|
||||||
;; ---- COMMAND: Recover Profile
|
;; ---- COMMAND: Recover Profile
|
||||||
|
|
||||||
(defn recover-profile
|
(defn recover-profile
|
||||||
[{:keys [pool tokens] :as cfg} {:keys [token password]}]
|
[{:keys [pool sprops] :as cfg} {:keys [token password]}]
|
||||||
(letfn [(validate-token [token]
|
(letfn [(validate-token [token]
|
||||||
(let [tdata (tokens :verify {:token token :iss :password-recovery})]
|
(let [tdata (tokens/verify sprops {:token token :iss :password-recovery})]
|
||||||
(:profile-id tdata)))
|
(:profile-id tdata)))
|
||||||
|
|
||||||
(update-password [conn profile-id]
|
(update-password [conn profile-id]
|
||||||
|
@ -184,12 +185,12 @@
|
||||||
;; ---- COMMAND: Prepare Register
|
;; ---- COMMAND: Prepare Register
|
||||||
|
|
||||||
(defn prepare-register
|
(defn prepare-register
|
||||||
[{:keys [pool tokens] :as cfg} params]
|
[{:keys [pool sprops] :as cfg} params]
|
||||||
(when-not (contains? cf/flags :registration)
|
(when-not (contains? cf/flags :registration)
|
||||||
(if-not (contains? params :invitation-token)
|
(if-not (contains? params :invitation-token)
|
||||||
(ex/raise :type :restriction
|
(ex/raise :type :restriction
|
||||||
:code :registration-disabled)
|
:code :registration-disabled)
|
||||||
(let [invitation (tokens :verify {:token (:invitation-token params) :iss :team-invitation})]
|
(let [invitation (tokens/verify sprops {:token (:invitation-token params) :iss :team-invitation})]
|
||||||
(when-not (= (:email params) (:member-email invitation))
|
(when-not (= (:email params) (:member-email invitation))
|
||||||
(ex/raise :type :restriction
|
(ex/raise :type :restriction
|
||||||
:code :email-does-not-match-invitation
|
:code :email-does-not-match-invitation
|
||||||
|
@ -222,7 +223,7 @@
|
||||||
:iss :prepared-register
|
:iss :prepared-register
|
||||||
:exp (dt/in-future "48h")}
|
:exp (dt/in-future "48h")}
|
||||||
|
|
||||||
token (tokens :generate params)]
|
token (tokens/generate sprops params)]
|
||||||
(with-meta {:token token}
|
(with-meta {:token token}
|
||||||
{::audit/profile-id uuid/zero})))
|
{::audit/profile-id uuid/zero})))
|
||||||
|
|
||||||
|
@ -297,8 +298,8 @@
|
||||||
(assoc :default-project-id (:default-project-id team)))))
|
(assoc :default-project-id (:default-project-id team)))))
|
||||||
|
|
||||||
(defn register-profile
|
(defn register-profile
|
||||||
[{:keys [conn tokens session] :as cfg} {:keys [token] :as params}]
|
[{:keys [conn sprops session] :as cfg} {:keys [token] :as params}]
|
||||||
(let [claims (tokens :verify {:token token :iss :prepared-register})
|
(let [claims (tokens/verify sprops {:token token :iss :prepared-register})
|
||||||
params (merge params claims)]
|
params (merge params claims)]
|
||||||
(check-profile-existence! conn params)
|
(check-profile-existence! conn params)
|
||||||
(let [is-active (or (:is-active params)
|
(let [is-active (or (:is-active params)
|
||||||
|
@ -308,14 +309,14 @@
|
||||||
(create-profile-relations conn)
|
(create-profile-relations conn)
|
||||||
(profile/decode-profile-row))
|
(profile/decode-profile-row))
|
||||||
invitation (when-let [token (:invitation-token params)]
|
invitation (when-let [token (:invitation-token params)]
|
||||||
(tokens :verify {:token token :iss :team-invitation}))]
|
(tokens/verify sprops {:token token :iss :team-invitation}))]
|
||||||
(cond
|
(cond
|
||||||
;; If invitation token comes in params, this is because the user comes from team-invitation process;
|
;; If invitation token comes in params, this is because the user comes from team-invitation process;
|
||||||
;; in this case, regenerate token and send back to the user a new invitation token (and mark current
|
;; in this case, regenerate token and send back to the user a new invitation token (and mark current
|
||||||
;; session as logged). This happens only if the invitation email matches with the register email.
|
;; session as logged). This happens only if the invitation email matches with the register email.
|
||||||
(and (some? invitation) (= (:email profile) (:member-email invitation)))
|
(and (some? invitation) (= (:email profile) (:member-email invitation)))
|
||||||
(let [claims (assoc invitation :member-id (:id profile))
|
(let [claims (assoc invitation :member-id (:id profile))
|
||||||
token (tokens :generate claims)
|
token (tokens/generate sprops claims)
|
||||||
resp {:invitation-token token}]
|
resp {:invitation-token token}]
|
||||||
(with-meta resp
|
(with-meta resp
|
||||||
{:transform-response ((:create session) (:id profile))
|
{:transform-response ((:create session) (:id profile))
|
||||||
|
@ -341,14 +342,15 @@
|
||||||
|
|
||||||
;; In all other cases, send a verification email.
|
;; In all other cases, send a verification email.
|
||||||
:else
|
:else
|
||||||
(let [vtoken (tokens :generate
|
(let [vtoken (tokens/generate sprops
|
||||||
{:iss :verify-email
|
{:iss :verify-email
|
||||||
:exp (dt/in-future "48h")
|
:exp (dt/in-future "48h")
|
||||||
:profile-id (:id profile)
|
:profile-id (:id profile)
|
||||||
:email (:email profile)})
|
:email (:email profile)})
|
||||||
ptoken (tokens :generate-predefined
|
ptoken (tokens/generate sprops
|
||||||
{:iss :profile-identity
|
{:iss :profile-identity
|
||||||
:profile-id (:id profile)})]
|
:profile-id (:id profile)
|
||||||
|
:exp (dt/in-future {:days 30})})]
|
||||||
(eml/send! {::eml/conn conn
|
(eml/send! {::eml/conn conn
|
||||||
::eml/factory eml/register
|
::eml/factory eml/register
|
||||||
:public-uri (:public-uri cfg)
|
:public-uri (:public-uri cfg)
|
||||||
|
@ -376,18 +378,19 @@
|
||||||
;; ---- COMMAND: Request Profile Recovery
|
;; ---- COMMAND: Request Profile Recovery
|
||||||
|
|
||||||
(defn request-profile-recovery
|
(defn request-profile-recovery
|
||||||
[{:keys [pool tokens] :as cfg} {:keys [email] :as params}]
|
[{:keys [pool sprops] :as cfg} {:keys [email] :as params}]
|
||||||
(letfn [(create-recovery-token [{:keys [id] :as profile}]
|
(letfn [(create-recovery-token [{:keys [id] :as profile}]
|
||||||
(let [token (tokens :generate
|
(let [token (tokens/generate sprops
|
||||||
{:iss :password-recovery
|
{:iss :password-recovery
|
||||||
:exp (dt/in-future "15m")
|
:exp (dt/in-future "15m")
|
||||||
:profile-id id})]
|
:profile-id id})]
|
||||||
(assoc profile :token token)))
|
(assoc profile :token token)))
|
||||||
|
|
||||||
(send-email-notification [conn profile]
|
(send-email-notification [conn profile]
|
||||||
(let [ptoken (tokens :generate-predefined
|
(let [ptoken (tokens/generate sprops
|
||||||
{:iss :profile-identity
|
{:iss :profile-identity
|
||||||
:profile-id (:id profile)})]
|
:profile-id (:id profile)
|
||||||
|
:exp (dt/in-future {:days 30})})]
|
||||||
(eml/send! {::eml/conn conn
|
(eml/send! {::eml/conn conn
|
||||||
::eml/factory eml/password-recovery
|
::eml/factory eml/password-recovery
|
||||||
:public-uri (:public-uri cfg)
|
:public-uri (:public-uri cfg)
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
[app.rpc.queries.profile :as profile]
|
[app.rpc.queries.profile :as profile]
|
||||||
[app.rpc.rlimit :as rlimit]
|
[app.rpc.rlimit :as rlimit]
|
||||||
[app.storage :as sto]
|
[app.storage :as sto]
|
||||||
|
[app.tokens :as tokens]
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
|
@ -183,15 +184,16 @@
|
||||||
{:changed true})
|
{:changed true})
|
||||||
|
|
||||||
(defn- request-email-change
|
(defn- request-email-change
|
||||||
[{:keys [conn tokens] :as cfg} {:keys [profile email] :as params}]
|
[{:keys [conn sprops] :as cfg} {:keys [profile email] :as params}]
|
||||||
(let [token (tokens :generate
|
(let [token (tokens/generate sprops
|
||||||
{:iss :change-email
|
{:iss :change-email
|
||||||
:exp (dt/in-future "15m")
|
:exp (dt/in-future "15m")
|
||||||
:profile-id (:id profile)
|
:profile-id (:id profile)
|
||||||
:email email})
|
:email email})
|
||||||
ptoken (tokens :generate-predefined
|
ptoken (tokens/generate sprops
|
||||||
{:iss :profile-identity
|
{:iss :profile-identity
|
||||||
:profile-id (:id profile)})]
|
:profile-id (:id profile)
|
||||||
|
:exp (dt/in-future {:days 30})})]
|
||||||
|
|
||||||
(when (not= email (:email profile))
|
(when (not= email (:email profile))
|
||||||
(cmd.auth/check-profile-existence! conn params))
|
(cmd.auth/check-profile-existence! conn params))
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
[app.rpc.queries.teams :as teams]
|
[app.rpc.queries.teams :as teams]
|
||||||
[app.rpc.rlimit :as rlimit]
|
[app.rpc.rlimit :as rlimit]
|
||||||
[app.storage :as sto]
|
[app.storage :as sto]
|
||||||
|
[app.tokens :as tokens]
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
|
@ -398,20 +399,21 @@
|
||||||
update set role = ?, valid_until = ?, updated_at = now();")
|
update set role = ?, valid_until = ?, updated_at = now();")
|
||||||
|
|
||||||
(defn- create-team-invitation
|
(defn- create-team-invitation
|
||||||
[{:keys [conn tokens team profile role email] :as cfg}]
|
[{:keys [conn sprops team profile role email] :as cfg}]
|
||||||
(let [member (profile/retrieve-profile-data-by-email conn email)
|
(let [member (profile/retrieve-profile-data-by-email conn email)
|
||||||
token-exp (dt/in-future "168h") ;; 7 days
|
token-exp (dt/in-future "168h") ;; 7 days
|
||||||
itoken (tokens :generate
|
itoken (tokens/generate sprops
|
||||||
{:iss :team-invitation
|
{:iss :team-invitation
|
||||||
:exp token-exp
|
:exp token-exp
|
||||||
:profile-id (:id profile)
|
:profile-id (:id profile)
|
||||||
:role role
|
:role role
|
||||||
:team-id (:id team)
|
:team-id (:id team)
|
||||||
:member-email (:email member email)
|
:member-email (:email member email)
|
||||||
:member-id (:id member)})
|
:member-id (:id member)})
|
||||||
ptoken (tokens :generate-predefined
|
ptoken (tokens/generate sprops
|
||||||
{:iss :profile-identity
|
{:iss :profile-identity
|
||||||
:profile-id (:id profile)})]
|
:profile-id (:id profile)
|
||||||
|
:exp (dt/in-future {:days 30})})]
|
||||||
|
|
||||||
(when (contains? cf/flags :log-invitation-tokens)
|
(when (contains? cf/flags :log-invitation-tokens)
|
||||||
(l/trace :hint "invitation token" :token itoken))
|
(l/trace :hint "invitation token" :token itoken))
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
[app.loggers.audit :as audit]
|
[app.loggers.audit :as audit]
|
||||||
[app.rpc.mutations.teams :as teams]
|
[app.rpc.mutations.teams :as teams]
|
||||||
[app.rpc.queries.profile :as profile]
|
[app.rpc.queries.profile :as profile]
|
||||||
|
[app.tokens :as tokens]
|
||||||
|
[app.tokens.spec.team-invitation :as-alias spec.team-invitation]
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
|
@ -23,9 +25,9 @@
|
||||||
:opt-un [::profile-id]))
|
:opt-un [::profile-id]))
|
||||||
|
|
||||||
(sv/defmethod ::verify-token {:auth false}
|
(sv/defmethod ::verify-token {:auth false}
|
||||||
[{:keys [pool tokens] :as cfg} {:keys [token] :as params}]
|
[{:keys [pool sprops] :as cfg} {:keys [token] :as params}]
|
||||||
(db/with-atomic [conn pool]
|
(db/with-atomic [conn pool]
|
||||||
(let [claims (tokens :verify {:token token})
|
(let [claims (tokens/verify sprops {:token token})
|
||||||
cfg (assoc cfg :conn conn)]
|
cfg (assoc cfg :conn conn)]
|
||||||
(process-token cfg params claims))))
|
(process-token cfg params claims))))
|
||||||
|
|
||||||
|
@ -76,19 +78,19 @@
|
||||||
(s/def ::iss keyword?)
|
(s/def ::iss keyword?)
|
||||||
(s/def ::exp ::us/inst)
|
(s/def ::exp ::us/inst)
|
||||||
|
|
||||||
(s/def :internal.tokens.team-invitation/profile-id ::us/uuid)
|
(s/def ::spec.team-invitation/profile-id ::us/uuid)
|
||||||
(s/def :internal.tokens.team-invitation/role ::us/keyword)
|
(s/def ::spec.team-invitation/role ::us/keyword)
|
||||||
(s/def :internal.tokens.team-invitation/team-id ::us/uuid)
|
(s/def ::spec.team-invitation/team-id ::us/uuid)
|
||||||
(s/def :internal.tokens.team-invitation/member-email ::us/email)
|
(s/def ::spec.team-invitation/member-email ::us/email)
|
||||||
(s/def :internal.tokens.team-invitation/member-id (s/nilable ::us/uuid))
|
(s/def ::spec.team-invitation/member-id (s/nilable ::us/uuid))
|
||||||
|
|
||||||
(s/def ::team-invitation-claims
|
(s/def ::team-invitation-claims
|
||||||
(s/keys :req-un [::iss ::exp
|
(s/keys :req-un [::iss ::exp
|
||||||
:internal.tokens.team-invitation/profile-id
|
::spec.team-invitation/profile-id
|
||||||
:internal.tokens.team-invitation/role
|
::spec.team-invitation/role
|
||||||
:internal.tokens.team-invitation/team-id
|
::spec.team-invitation/team-id
|
||||||
:internal.tokens.team-invitation/member-email]
|
::spec.team-invitation/member-email]
|
||||||
:opt-un [:internal.tokens.team-invitation/member-id]))
|
:opt-un [::spec.team-invitation/member-id]))
|
||||||
|
|
||||||
(defn- accept-invitation
|
(defn- accept-invitation
|
||||||
[{:keys [conn] :as cfg} {:keys [member-id team-id role member-email] :as claims}]
|
[{:keys [conn] :as cfg} {:keys [member-id team-id role member-email] :as claims}]
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.setup.builtin-templates]
|
[app.setup.builtin-templates]
|
||||||
|
[app.setup.keys :as keys]
|
||||||
[buddy.core.codecs :as bc]
|
[buddy.core.codecs :as bc]
|
||||||
[buddy.core.nonce :as bn]
|
[buddy.core.nonce :as bn]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
|
@ -59,6 +60,8 @@
|
||||||
"all sessions on each restart, it is hightly recommeded setting up the "
|
"all sessions on each restart, it is hightly recommeded setting up the "
|
||||||
"PENPOT_SECRET_KEY environment variable")))
|
"PENPOT_SECRET_KEY environment variable")))
|
||||||
|
|
||||||
(let [stored (-> (retrieve-all conn)
|
(let [secret (or key (generate-random-key))]
|
||||||
(assoc :secret-key (or key (generate-random-key))))]
|
(-> (retrieve-all conn)
|
||||||
(update stored :instance-id handle-instance-id conn (db/read-only? pool)))))
|
(assoc :secret-key secret)
|
||||||
|
(assoc :tokens-key (keys/derive secret :salt "tokens" :size 32))
|
||||||
|
(update :instance-id handle-instance-id conn (db/read-only? pool))))))
|
||||||
|
|
|
@ -6,24 +6,17 @@
|
||||||
|
|
||||||
(ns app.setup.keys
|
(ns app.setup.keys
|
||||||
"Keys derivation service."
|
"Keys derivation service."
|
||||||
|
(:refer-clojure :exclude [derive])
|
||||||
(:require
|
(:require
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[buddy.core.kdf :as bk]
|
[buddy.core.kdf :as bk]))
|
||||||
[clojure.spec.alpha :as s]
|
|
||||||
[integrant.core :as ig]))
|
|
||||||
|
|
||||||
(s/def ::secret-key ::us/string)
|
|
||||||
(s/def ::props (s/keys :req-un [::secret-key]))
|
|
||||||
|
|
||||||
(defmethod ig/pre-init-spec :app.setup/keys [_]
|
|
||||||
(s/keys :req-un [::props]))
|
|
||||||
|
|
||||||
(defmethod ig/init-key :app.setup/keys
|
|
||||||
[_ {:keys [props] :as cfg}]
|
|
||||||
(fn [& {:keys [salt _]}]
|
|
||||||
(let [engine (bk/engine {:key (:secret-key props)
|
|
||||||
:salt salt
|
|
||||||
:alg :hkdf
|
|
||||||
:digest :blake2b-512})]
|
|
||||||
(bk/get-bytes engine 32))))
|
|
||||||
|
|
||||||
|
(defn derive
|
||||||
|
"Derive a key from secret-key"
|
||||||
|
[secret-key & {:keys [salt size]}]
|
||||||
|
(us/assert! ::us/not-empty-string secret-key)
|
||||||
|
(let [engine (bk/engine {:key secret-key
|
||||||
|
:salt salt
|
||||||
|
:alg :hkdf
|
||||||
|
:digest :blake2b-512})]
|
||||||
|
(bk/get-bytes engine size)))
|
||||||
|
|
|
@ -5,28 +5,27 @@
|
||||||
;; Copyright (c) UXBOX Labs SL
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.tokens
|
(ns app.tokens
|
||||||
"Tokens generation service."
|
"Tokens generation API."
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.transit :as t]
|
[app.common.transit :as t]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[buddy.sign.jwe :as jwe]
|
[buddy.sign.jwe :as jwe]))
|
||||||
[clojure.spec.alpha :as s]
|
|
||||||
[integrant.core :as ig]))
|
|
||||||
|
|
||||||
(defn- generate
|
(defn generate
|
||||||
[cfg claims]
|
[{:keys [tokens-key]} claims]
|
||||||
|
(us/assert! ::us/not-empty-string tokens-key)
|
||||||
(let [payload (-> claims
|
(let [payload (-> claims
|
||||||
(assoc :iat (dt/now))
|
(assoc :iat (dt/now))
|
||||||
(d/without-nils)
|
(d/without-nils)
|
||||||
(t/encode))]
|
(t/encode))]
|
||||||
(jwe/encrypt payload (::secret cfg) {:alg :a256kw :enc :a256gcm})))
|
(jwe/encrypt payload tokens-key {:alg :a256kw :enc :a256gcm})))
|
||||||
|
|
||||||
(defn- verify
|
(defn verify
|
||||||
[cfg {:keys [token] :as params}]
|
[{:keys [tokens-key]} {:keys [token] :as params}]
|
||||||
(let [payload (jwe/decrypt token (::secret cfg) {:alg :a256kw :enc :a256gcm})
|
(let [payload (jwe/decrypt token tokens-key {:alg :a256kw :enc :a256gcm})
|
||||||
claims (t/decode payload)]
|
claims (t/decode payload)]
|
||||||
(when (and (dt/instant? (:exp claims))
|
(when (and (dt/instant? (:exp claims))
|
||||||
(dt/is-before? (:exp claims) (dt/now)))
|
(dt/is-before? (:exp claims) (dt/now)))
|
||||||
|
@ -45,30 +44,7 @@
|
||||||
:params params))
|
:params params))
|
||||||
claims))
|
claims))
|
||||||
|
|
||||||
(defn- generate-predefined
|
|
||||||
[cfg {:keys [iss profile-id] :as params}]
|
|
||||||
(case iss
|
|
||||||
:profile-identity
|
|
||||||
(do
|
|
||||||
(us/verify uuid? profile-id)
|
|
||||||
(generate cfg (assoc params
|
|
||||||
:exp (dt/in-future {:days 30}))))
|
|
||||||
|
|
||||||
(ex/raise :type :internal
|
|
||||||
:code :not-implemented
|
|
||||||
:hint "no predefined token")))
|
|
||||||
|
|
||||||
(s/def ::keys fn?)
|
|
||||||
|
|
||||||
(defmethod ig/pre-init-spec ::tokens [_]
|
|
||||||
(s/keys :req-un [::keys]))
|
|
||||||
|
|
||||||
(defmethod ig/init-key ::tokens
|
|
||||||
[_ {:keys [keys] :as cfg}]
|
|
||||||
(let [secret (keys :salt "tokens" :size 32)
|
|
||||||
cfg (assoc cfg ::secret secret)]
|
|
||||||
(fn [action params]
|
|
||||||
(case action
|
|
||||||
:generate-predefined (generate-predefined cfg params)
|
|
||||||
:verify (verify cfg params)
|
|
||||||
:generate (generate cfg params)))))
|
|
||||||
|
|
|
@ -116,6 +116,7 @@
|
||||||
(some? position-modifier)
|
(some? position-modifier)
|
||||||
(gpt/transform position-modifier))
|
(gpt/transform position-modifier))
|
||||||
content (:content draft)
|
content (:content draft)
|
||||||
|
|
||||||
pos-x (* (:x position) zoom)
|
pos-x (* (:x position) zoom)
|
||||||
pos-y (* (:y position) zoom)
|
pos-y (* (:y position) zoom)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue