mirror of
https://github.com/penpot/penpot.git
synced 2025-03-31 00:51:19 -05:00
✨ Improve auth module.
This commit is contained in:
parent
d5ff5ea91e
commit
5858f3f180
12 changed files with 269 additions and 254 deletions
|
@ -12,7 +12,6 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.spec :as us]
|
||||
[app.config :as cfg]
|
||||
[app.http.auth :as auth]
|
||||
[app.http.errors :as errors]
|
||||
[app.http.middleware :as middleware]
|
||||
[app.metrics :as mtx]
|
||||
|
@ -147,9 +146,6 @@
|
|||
["/github" {:post (:auth-handler github-auth)}]
|
||||
["/github/callback" {:get (:callback-handler github-auth)}]]
|
||||
|
||||
["/login" {:post #(auth/login-handler cfg %)}]
|
||||
["/logout" {:post #(auth/logout-handler cfg %)}]
|
||||
|
||||
["/login-ldap" {:post ldap-auth}]
|
||||
|
||||
["/rpc" {:middleware [(:middleware session)]}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.http.auth
|
||||
(:require
|
||||
[app.http.session :as session]))
|
||||
|
||||
(defn login-handler
|
||||
[{:keys [session rpc] :as cfg} request]
|
||||
(let [data (:params request)
|
||||
uagent (get-in request [:headers "user-agent"])
|
||||
method (get-in rpc [:methods :mutation :login])
|
||||
profile (method data)
|
||||
id (session/create! session {:profile-id (:id profile)
|
||||
:user-agent uagent})]
|
||||
{:status 200
|
||||
:cookies (session/cookies session {:value id})
|
||||
:body profile}))
|
||||
|
||||
(defn logout-handler
|
||||
[{:keys [session] :as cfg} request]
|
||||
(session/delete! cfg request)
|
||||
{:status 204
|
||||
:cookies (session/cookies session {:value "" :max-age -1})
|
||||
:body ""})
|
|
@ -12,7 +12,6 @@
|
|||
[app.common.exceptions :as ex]
|
||||
[app.common.spec :as us]
|
||||
[app.config :as cfg]
|
||||
[app.http.session :as session]
|
||||
[app.util.http :as http]
|
||||
[app.util.time :as dt]
|
||||
[clojure.data.json :as json]
|
||||
|
@ -38,7 +37,6 @@
|
|||
|
||||
(def scope "user:email")
|
||||
|
||||
|
||||
(defn- build-redirect-url
|
||||
[cfg]
|
||||
(let [public (u/uri (:public-uri cfg))]
|
||||
|
@ -46,57 +44,47 @@
|
|||
|
||||
(defn- get-access-token
|
||||
[cfg state code]
|
||||
(let [params {:client_id (:client-id cfg)
|
||||
:client_secret (:client-secret cfg)
|
||||
:code code
|
||||
:state state
|
||||
:redirect_uri (build-redirect-url cfg)}
|
||||
req {:method :post
|
||||
:headers {"content-type" "application/x-www-form-urlencoded"
|
||||
"accept" "application/json"}
|
||||
:uri (str token-url)
|
||||
:body (u/map->query-string params)}
|
||||
res (http/send! req)]
|
||||
(try
|
||||
(let [params {:client_id (:client-id cfg)
|
||||
:client_secret (:client-secret cfg)
|
||||
:code code
|
||||
:state state
|
||||
:redirect_uri (build-redirect-url cfg)}
|
||||
req {:method :post
|
||||
:headers {"content-type" "application/x-www-form-urlencoded"
|
||||
"accept" "application/json"}
|
||||
:uri (str token-url)
|
||||
:timeout 6000
|
||||
:body (u/map->query-string params)}
|
||||
res (http/send! req)]
|
||||
|
||||
(when (not= 200 (:status res))
|
||||
(ex/raise :type :internal
|
||||
:code :invalid-response-from-github
|
||||
:context {:status (:status res)
|
||||
:body (:body res)}))
|
||||
(try
|
||||
(let [data (json/read-str (:body res))]
|
||||
(get data "access_token"))
|
||||
(catch Throwable e
|
||||
(log/error "unexpected error on parsing response body from github access token request" e)
|
||||
nil))))
|
||||
(when (= 200 (:status res))
|
||||
(-> (json/read-str (:body res))
|
||||
(get "access_token"))))
|
||||
|
||||
(catch Exception e
|
||||
(log/error e "unexpected error on get-access-token")
|
||||
nil)))
|
||||
|
||||
(defn- get-user-info
|
||||
[token]
|
||||
(let [req {:uri (str user-info-url)
|
||||
:headers {"authorization" (str "token " token)}
|
||||
:method :get}
|
||||
res (http/send! req)]
|
||||
|
||||
(when (not= 200 (:status res))
|
||||
(ex/raise :type :internal
|
||||
:code :invalid-response-from-github
|
||||
:context {:status (:status res)
|
||||
:body (:body res)}))
|
||||
|
||||
(try
|
||||
(let [data (json/read-str (:body res))]
|
||||
{:email (get data "email")
|
||||
:fullname (get data "name")})
|
||||
(catch Throwable e
|
||||
(log/error "unexpected error on parsing response body from github access token request" e)
|
||||
nil))))
|
||||
(try
|
||||
(let [req {:uri (str user-info-url)
|
||||
:headers {"authorization" (str "token " token)}
|
||||
:timeout 6000
|
||||
:method :get}
|
||||
res (http/send! req)]
|
||||
(when (= 200 (:status res))
|
||||
(let [data (json/read-str (:body res))]
|
||||
{:email (get data "email")
|
||||
:fullname (get data "name")})))
|
||||
(catch Exception e
|
||||
(log/error e "unexpected exception on get-user-info")
|
||||
nil)))
|
||||
|
||||
(defn auth
|
||||
[{:keys [tokens] :as cfg} _request]
|
||||
(let [state (tokens :generate
|
||||
{:iss :github-oauth
|
||||
:exp (dt/in-future "15m")})
|
||||
|
||||
(let [state (tokens :generate {:iss :github-oauth :exp (dt/in-future "15m")})
|
||||
params {:client_id (:client-id cfg/config)
|
||||
:redirect_uri (build-redirect-url cfg)
|
||||
:state state
|
||||
|
@ -109,37 +97,37 @@
|
|||
|
||||
(defn callback
|
||||
[{:keys [tokens rpc session] :as cfg} request]
|
||||
(let [state (get-in request [:params :state])
|
||||
_ (tokens :verify {:token state :iss :github-oauth})
|
||||
info (some->> (get-in request [:params :code])
|
||||
(get-access-token cfg state)
|
||||
(get-user-info))]
|
||||
(try
|
||||
(let [state (get-in request [:params :state])
|
||||
_ (tokens :verify {:token state :iss :github-oauth})
|
||||
info (some->> (get-in request [:params :code])
|
||||
(get-access-token cfg state)
|
||||
(get-user-info))
|
||||
|
||||
(when-not info
|
||||
(ex/raise :type :authentication
|
||||
:code :unable-to-authenticate-with-github))
|
||||
_ (when-not info
|
||||
(ex/raise :type :internal
|
||||
:code :unable-to-auth))
|
||||
|
||||
(let [method-fn (get-in rpc [:methods :mutation :login-or-register])
|
||||
method-fn (get-in rpc [:methods :mutation :login-or-register])
|
||||
profile (method-fn {:email (:email info)
|
||||
:fullname (:fullname info)})
|
||||
uagent (get-in request [:headers "user-agent"])
|
||||
|
||||
token (tokens :generate
|
||||
{:iss :auth
|
||||
:exp (dt/in-future "15m")
|
||||
:profile-id (:id profile)})
|
||||
|
||||
uri (-> (u/uri (:public-uri cfg/config))
|
||||
(assoc :path "/#/auth/verify-token")
|
||||
(assoc :query (u/map->query-string {:token token})))
|
||||
|
||||
sid (session/create! session {:profile-id (:id profile)
|
||||
:user-agent uagent})]
|
||||
|
||||
{:status 302
|
||||
:headers {"location" (str uri)}
|
||||
:cookies (session/cookies session/cookies {:value sid})
|
||||
:body ""})))
|
||||
sxf ((:create session) (:id profile))
|
||||
rsp {:status 302 :headers {"location" (str uri)} :body ""}]
|
||||
(sxf request rsp))
|
||||
(catch Exception _e
|
||||
(let [uri (-> (u/uri (:public-uri cfg))
|
||||
(assoc :path "/#/auth/login")
|
||||
(assoc :query (u/map->query-string {:error "unable-to-auth"})))]
|
||||
{:status 302
|
||||
:headers {"location" (str uri)}
|
||||
:body ""}))))
|
||||
|
||||
;; --- ENTRY POINT
|
||||
|
||||
|
|
|
@ -12,88 +12,75 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.spec :as us]
|
||||
[app.http.session :as session]
|
||||
[app.util.http :as http]
|
||||
[app.util.time :as dt]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.tools.logging :as log]
|
||||
[integrant.core :as ig]
|
||||
[lambdaisland.uri :as uri]))
|
||||
[lambdaisland.uri :as u]))
|
||||
|
||||
(def scope "read_user")
|
||||
|
||||
(defn- build-redirect-url
|
||||
[cfg]
|
||||
(let [public (uri/uri (:public-uri cfg))]
|
||||
(let [public (u/uri (:public-uri cfg))]
|
||||
(str (assoc public :path "/api/oauth/gitlab/callback"))))
|
||||
|
||||
|
||||
(defn- build-oauth-uri
|
||||
[cfg]
|
||||
(let [base-uri (uri/uri (:base-uri cfg))]
|
||||
(let [base-uri (u/uri (:base-uri cfg))]
|
||||
(assoc base-uri :path "/oauth/authorize")))
|
||||
|
||||
|
||||
(defn- build-token-url
|
||||
[cfg]
|
||||
(let [base-uri (uri/uri (:base-uri cfg))]
|
||||
(let [base-uri (u/uri (:base-uri cfg))]
|
||||
(str (assoc base-uri :path "/oauth/token"))))
|
||||
|
||||
|
||||
(defn- build-user-info-url
|
||||
[cfg]
|
||||
(let [base-uri (uri/uri (:base-uri cfg))]
|
||||
(let [base-uri (u/uri (:base-uri cfg))]
|
||||
(str (assoc base-uri :path "/api/v4/user"))))
|
||||
|
||||
(defn- get-access-token
|
||||
[cfg code]
|
||||
(let [params {:client_id (:client-id cfg)
|
||||
:client_secret (:client-secret cfg)
|
||||
:code code
|
||||
:grant_type "authorization_code"
|
||||
:redirect_uri (build-redirect-url cfg)}
|
||||
req {:method :post
|
||||
:headers {"content-type" "application/x-www-form-urlencoded"}
|
||||
:uri (build-token-url cfg)
|
||||
:body (uri/map->query-string params)}
|
||||
(try
|
||||
(let [params {:client_id (:client-id cfg)
|
||||
:client_secret (:client-secret cfg)
|
||||
:code code
|
||||
:grant_type "authorization_code"
|
||||
:redirect_uri (build-redirect-url cfg)}
|
||||
req {:method :post
|
||||
:headers {"content-type" "application/x-www-form-urlencoded"}
|
||||
:uri (build-token-url cfg)
|
||||
:body (u/map->query-string params)}
|
||||
res (http/send! req)]
|
||||
|
||||
(when (not= 200 (:status res))
|
||||
(ex/raise :type :internal
|
||||
:code :invalid-response-from-gitlab
|
||||
:context {:status (:status res)
|
||||
:body (:body res)}))
|
||||
|
||||
(try
|
||||
(let [data (json/read-str (:body res))]
|
||||
(get data "access_token"))
|
||||
(catch Throwable e
|
||||
(log/error "unexpected error on parsing response body from gitlab access token request" e)
|
||||
nil))))
|
||||
(when (= 200 (:status res))
|
||||
(-> (json/read-str (:body res))
|
||||
(get "access_token"))))
|
||||
|
||||
(catch Exception e
|
||||
(log/error e "unexpected error on get-access-token")
|
||||
nil)))
|
||||
|
||||
(defn- get-user-info
|
||||
[cfg token]
|
||||
(let [req {:uri (build-user-info-url cfg)
|
||||
:headers {"Authorization" (str "Bearer " token)}
|
||||
:method :get}
|
||||
res (http/send! req)]
|
||||
(try
|
||||
(let [req {:uri (build-user-info-url cfg)
|
||||
:headers {"Authorization" (str "Bearer " token)}
|
||||
:timeout 6000
|
||||
:method :get}
|
||||
res (http/send! req)]
|
||||
|
||||
(when (not= 200 (:status res))
|
||||
(ex/raise :type :internal
|
||||
:code :invalid-response-from-gitlab
|
||||
:context {:status (:status res)
|
||||
:body (:body res)}))
|
||||
(when (= 200 (:status res))
|
||||
(let [data (json/read-str (:body res))]
|
||||
{:email (get data "email")
|
||||
:fullname (get data "name")})))
|
||||
|
||||
(try
|
||||
(let [data (json/read-str (:body res))]
|
||||
;; (clojure.pprint/pprint data)
|
||||
{:email (get data "email")
|
||||
:fullname (get data "name")})
|
||||
(catch Throwable e
|
||||
(log/error "unexpected error on parsing response body from gitlab access token request" e)
|
||||
nil))))
|
||||
(catch Exception e
|
||||
(log/error e "unexpected exception on get-user-info")
|
||||
nil)))
|
||||
|
||||
(defn auth
|
||||
[{:keys [tokens] :as cfg} _request]
|
||||
|
@ -105,7 +92,7 @@
|
|||
:response_type "code"
|
||||
:state token
|
||||
:scope scope}
|
||||
query (uri/map->query-string params)
|
||||
query (u/map->query-string params)
|
||||
uri (-> (build-oauth-uri cfg)
|
||||
(assoc :query query))]
|
||||
{:status 200
|
||||
|
@ -113,36 +100,37 @@
|
|||
|
||||
(defn callback
|
||||
[{:keys [tokens rpc session] :as cfg} request]
|
||||
(let [token (get-in request [:params :state])
|
||||
_ (tokens :verify {:token token :iss :gitlab-oauth})
|
||||
info (some->> (get-in request [:params :code])
|
||||
(get-access-token cfg)
|
||||
(get-user-info cfg))]
|
||||
(try
|
||||
(let [token (get-in request [:params :state])
|
||||
_ (tokens :verify {:token token :iss :gitlab-oauth})
|
||||
info (some->> (get-in request [:params :code])
|
||||
(get-access-token cfg)
|
||||
(get-user-info cfg))
|
||||
_ (when-not info
|
||||
(ex/raise :type :internal
|
||||
:code :unable-to-auth))
|
||||
|
||||
(when-not info
|
||||
(ex/raise :type :authentication
|
||||
:code :unable-to-authenticate-with-gitlab))
|
||||
|
||||
(let [method-fn (get-in rpc [:methods :mutation :login-or-register])
|
||||
method-fn (get-in rpc [:methods :mutation :login-or-register])
|
||||
profile (method-fn {:email (:email info)
|
||||
:fullname (:fullname info)})
|
||||
uagent (get-in request [:headers "user-agent"])
|
||||
|
||||
token (tokens :generate {:iss :auth
|
||||
:exp (dt/in-future "15m")
|
||||
:profile-id (:id profile)})
|
||||
|
||||
uri (-> (uri/uri (:public-uri cfg))
|
||||
uri (-> (u/uri (:public-uri cfg))
|
||||
(assoc :path "/#/auth/verify-token")
|
||||
(assoc :query (uri/map->query-string {:token token})))
|
||||
|
||||
sid (session/create! session {:profile-id (:id profile)
|
||||
:user-agent uagent})]
|
||||
{:status 302
|
||||
:headers {"location" (str uri)}
|
||||
:cookies (session/cookies session {:value sid})
|
||||
:body ""})))
|
||||
(assoc :query (u/map->query-string {:token token})))
|
||||
|
||||
sxf ((:create session) (:id profile))
|
||||
rsp {:status 302 :headers {"location" (str uri)} :body ""}]
|
||||
(sxf request rsp))
|
||||
(catch Exception _e
|
||||
(let [uri (-> (u/uri (:public-uri cfg))
|
||||
(assoc :path "/#/auth/login")
|
||||
(assoc :query (u/map->query-string {:error "unable-to-auth"})))]
|
||||
{:status 302
|
||||
:headers {"location" (str uri)}
|
||||
:body ""}))))
|
||||
|
||||
(s/def ::client-id ::us/not-empty-string)
|
||||
(s/def ::client-secret ::us/not-empty-string)
|
||||
|
|
|
@ -11,14 +11,13 @@
|
|||
(:require
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.spec :as us]
|
||||
[app.http.session :as session]
|
||||
[app.util.http :as http]
|
||||
[app.util.time :as dt]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.tools.logging :as log]
|
||||
[integrant.core :as ig]
|
||||
[lambdaisland.uri :as uri]))
|
||||
[lambdaisland.uri :as u]))
|
||||
|
||||
(def base-goauth-uri "https://accounts.google.com/o/oauth2/v2/auth")
|
||||
|
||||
|
@ -30,7 +29,7 @@
|
|||
|
||||
(defn- build-redirect-url
|
||||
[cfg]
|
||||
(let [public (uri/uri (:public-uri cfg))]
|
||||
(let [public (u/uri (:public-uri cfg))]
|
||||
(str (assoc public :path "/api/oauth/google/callback"))))
|
||||
|
||||
(defn- get-access-token
|
||||
|
@ -44,7 +43,7 @@
|
|||
req {:method :post
|
||||
:headers {"content-type" "application/x-www-form-urlencoded"}
|
||||
:uri "https://oauth2.googleapis.com/token"
|
||||
:body (uri/map->query-string params)}
|
||||
:body (u/map->query-string params)}
|
||||
res (http/send! req)]
|
||||
|
||||
(when (= 200 (:status res))
|
||||
|
@ -80,8 +79,8 @@
|
|||
:response_type "code"
|
||||
:redirect_uri (build-redirect-url cfg)
|
||||
:client_id (:client-id cfg)}
|
||||
query (uri/map->query-string params)
|
||||
uri (-> (uri/uri base-goauth-uri)
|
||||
query (u/map->query-string params)
|
||||
uri (-> (u/uri base-goauth-uri)
|
||||
(assoc :query query))]
|
||||
{:status 200
|
||||
:body {:redirect-uri (str uri)}}))
|
||||
|
@ -100,24 +99,20 @@
|
|||
method-fn (get-in rpc [:methods :mutation :login-or-register])
|
||||
profile (method-fn {:email (:email info)
|
||||
:fullname (:fullname info)})
|
||||
uagent (get-in request [:headers "user-agent"])
|
||||
token (tokens :generate {:iss :auth
|
||||
:exp (dt/in-future "15m")
|
||||
:profile-id (:id profile)})
|
||||
uri (-> (uri/uri (:public-uri cfg))
|
||||
uri (-> (u/uri (:public-uri cfg))
|
||||
(assoc :path "/#/auth/verify-token")
|
||||
(assoc :query (uri/map->query-string {:token token})))
|
||||
(assoc :query (u/map->query-string {:token token})))
|
||||
|
||||
sid (session/create! session {:profile-id (:id profile)
|
||||
:user-agent uagent})]
|
||||
{:status 302
|
||||
:headers {"location" (str uri)}
|
||||
:cookies (session/cookies session {:value sid})
|
||||
:body ""})
|
||||
sxf ((:create session) (:id profile))
|
||||
rsp {:status 302 :headers {"location" (str uri)} :body ""}]
|
||||
(sxf request rsp))
|
||||
(catch Exception _e
|
||||
(let [uri (-> (uri/uri (:public-uri cfg))
|
||||
(let [uri (-> (u/uri (:public-uri cfg))
|
||||
(assoc :path "/#/auth/login")
|
||||
(assoc :query (uri/map->query-string {:error "unable-to-auth"})))]
|
||||
(assoc :query (u/map->query-string {:error "unable-to-auth"})))]
|
||||
{:status 302
|
||||
:headers {"location" (str uri)}
|
||||
:body ""}))))
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
(:require
|
||||
[app.common.exceptions :as ex]
|
||||
[app.config :as cfg]
|
||||
[app.http.session :as session]
|
||||
[clj-ldap.client :as client]
|
||||
[clojure.set :as set]
|
||||
[clojure.spec.alpha :as s]
|
||||
|
@ -66,12 +65,10 @@
|
|||
(let [method-fn (get-in rpc [:methods :mutation :login-or-register])
|
||||
profile (method-fn {:email (:email info)
|
||||
:fullname (:fullname info)})
|
||||
uagent (get-in request [:headers "user-agent"])
|
||||
sid (session/create! session {:profile-id (:id profile)
|
||||
:user-agent uagent})]
|
||||
{:status 200
|
||||
:cookies (session/cookies session {:value sid})
|
||||
:body profile}))))
|
||||
|
||||
sxf ((:create session) (:id profile))
|
||||
rsp {:status 200 :body profile}]
|
||||
(sxf request rsp)))))
|
||||
{::conn conn})))
|
||||
|
||||
(defmethod ig/halt-key! ::client
|
||||
|
|
|
@ -16,14 +16,16 @@
|
|||
[clojure.spec.alpha :as s]
|
||||
[integrant.core :as ig]))
|
||||
|
||||
(defn next-session-id
|
||||
;; --- IMPL
|
||||
|
||||
(defn- next-session-id
|
||||
([] (next-session-id 96))
|
||||
([n]
|
||||
(-> (bn/random-nonce n)
|
||||
(bc/bytes->b64u)
|
||||
(bc/bytes->str))))
|
||||
|
||||
(defn create!
|
||||
(defn- create
|
||||
[{:keys [conn] :as cfg} {:keys [profile-id user-agent]}]
|
||||
(let [id (next-session-id)]
|
||||
(db/insert! conn :http-session {:id id
|
||||
|
@ -31,28 +33,28 @@
|
|||
:user-agent user-agent})
|
||||
id))
|
||||
|
||||
(defn delete!
|
||||
[{:keys [conn cookie-name] :as cfg} request]
|
||||
(when-let [token (get-in request [:cookies cookie-name :value])]
|
||||
(defn- delete
|
||||
[{:keys [conn cookie-name] :as cfg} {:keys [cookies] :as request}]
|
||||
(when-let [token (get-in cookies [cookie-name :value])]
|
||||
(db/delete! conn :http-session {:id token}))
|
||||
nil)
|
||||
|
||||
(defn retrieve
|
||||
(defn- retrieve
|
||||
[{:keys [conn] :as cfg} token]
|
||||
(when token
|
||||
(-> (db/exec-one! conn ["select profile_id from http_session where id = ?" token])
|
||||
(:profile-id))))
|
||||
|
||||
(defn retrieve-from-request
|
||||
[{:keys [cookie-name] :as cfg} request]
|
||||
(->> (get-in request [:cookies cookie-name :value])
|
||||
(defn- retrieve-from-request
|
||||
[{:keys [cookie-name] :as cfg} {:keys [cookies] :as request}]
|
||||
(->> (get-in cookies [cookie-name :value])
|
||||
(retrieve cfg)))
|
||||
|
||||
(defn cookies
|
||||
(defn- cookies
|
||||
[{:keys [cookie-name] :as cfg} vals]
|
||||
{cookie-name (merge vals {:path "/" :http-only true})})
|
||||
|
||||
(defn middleware
|
||||
(defn- middleware
|
||||
[cfg handler]
|
||||
(fn [request]
|
||||
(if-let [profile-id (retrieve-from-request cfg request)]
|
||||
|
@ -61,6 +63,8 @@
|
|||
(handler (assoc request :profile-id profile-id)))
|
||||
(handler request))))
|
||||
|
||||
;; --- STATE INIT
|
||||
|
||||
(defmethod ig/pre-init-spec ::session [_]
|
||||
(s/keys :req-un [::db/pool]))
|
||||
|
||||
|
@ -71,4 +75,17 @@
|
|||
(defmethod ig/init-key ::session
|
||||
[_ {:keys [pool] :as cfg}]
|
||||
(let [cfg (assoc cfg :conn pool)]
|
||||
(merge cfg {:middleware #(middleware cfg %)})))
|
||||
(-> cfg
|
||||
(assoc :middleware #(middleware cfg %))
|
||||
(assoc :create (fn [profile-id]
|
||||
(fn [request response]
|
||||
(let [uagent (get-in request [:headers "user-agent"])
|
||||
value (create cfg {:profile-id profile-id :user-agent uagent})]
|
||||
(assoc response :cookies (cookies cfg {:value value}))))))
|
||||
(assoc :delete (fn [request response]
|
||||
(delete cfg request)
|
||||
(assoc response
|
||||
:status 204
|
||||
:body ""
|
||||
:cookies (cookies cfg {:value "" :max-age -1})))))))
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
[app.db :as db]
|
||||
[app.db.profile-initial-data :refer [create-profile-initial-data]]
|
||||
[app.emails :as emails]
|
||||
[app.http.session :as session]
|
||||
[app.media :as media]
|
||||
[app.rpc.mutations.projects :as projects]
|
||||
[app.rpc.mutations.teams :as teams]
|
||||
|
@ -95,13 +94,7 @@
|
|||
(with-meta (assoc profile
|
||||
:is-active true
|
||||
:claims claims)
|
||||
{:transform-response
|
||||
(fn [request response]
|
||||
(let [uagent (get-in request [:headers "user-agent"])
|
||||
id (session/create! session {:profile-id (:id profile)
|
||||
:user-agent uagent})]
|
||||
(assoc response
|
||||
:cookies (session/cookies session {:value id}))))}))
|
||||
{:transform-response ((:create session) (:id profile))}))
|
||||
|
||||
;; If no token is provided, send a verification email
|
||||
(let [token (tokens :generate
|
||||
|
@ -217,7 +210,7 @@
|
|||
:opt-un [::scope]))
|
||||
|
||||
(sv/defmethod ::login {:auth false :rlimit :password}
|
||||
[{:keys [pool] :as cfg} {:keys [email password scope] :as params}]
|
||||
[{:keys [pool session] :as cfg} {:keys [email password scope] :as params}]
|
||||
(letfn [(check-password [profile password]
|
||||
(when (= (:password profile) "!")
|
||||
(ex/raise :type :validation
|
||||
|
@ -240,8 +233,21 @@
|
|||
(let [prof (-> (profile/retrieve-profile-data-by-email conn email)
|
||||
(validate-profile)
|
||||
(profile/strip-private-attrs))
|
||||
addt (profile/retrieve-additional-data conn (:id prof))]
|
||||
(merge prof addt)))))
|
||||
addt (profile/retrieve-additional-data conn (:id prof))
|
||||
prof (merge prof addt)]
|
||||
(with-meta prof
|
||||
{:transform-response ((:create session) (:id prof))})))))
|
||||
|
||||
|
||||
;; --- Mutation: Logout
|
||||
|
||||
(s/def ::logout
|
||||
(s/keys :req-un [::profile-id]))
|
||||
|
||||
(sv/defmethod ::logout
|
||||
[{:keys [pool session] :as cfg} {:keys [profile-id] :as params}]
|
||||
(with-meta {}
|
||||
{:transform-response (:delete session)}))
|
||||
|
||||
|
||||
;; --- Mutation: Register if not exists
|
||||
|
@ -480,11 +486,7 @@
|
|||
{:id profile-id})
|
||||
|
||||
(with-meta {}
|
||||
{:transform-response
|
||||
(fn [request response]
|
||||
(session/delete! session request)
|
||||
(assoc response
|
||||
:cookies (session/cookies session {:value "" :max-age -1})))})))
|
||||
{:transform-response (:delete session)})))
|
||||
|
||||
(def sql:owned-teams
|
||||
"with owner_teams as (
|
||||
|
|
|
@ -5,14 +5,13 @@
|
|||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||
|
||||
(ns app.rpc.mutations.verify-token
|
||||
(:require
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.spec :as us]
|
||||
[app.db :as db]
|
||||
[app.http.session :as session]
|
||||
[app.rpc.mutations.teams :as teams]
|
||||
[app.rpc.queries.profile :as profile]
|
||||
[app.util.services :as sv]
|
||||
|
@ -57,14 +56,7 @@
|
|||
{:id (:id profile)}))
|
||||
|
||||
(with-meta claims
|
||||
{:transform-response
|
||||
(fn [request response]
|
||||
(let [uagent (get-in request [:headers "user-agent"])
|
||||
id (session/create! session {:profile-id profile-id
|
||||
:user-agent uagent})]
|
||||
(assoc response
|
||||
:cookies (session/cookies session {:value id}))))})))
|
||||
|
||||
{:transform-response ((:create session) profile-id)})))
|
||||
|
||||
(defmethod process-token :auth
|
||||
[{:keys [conn] :as cfg} _params {:keys [profile-id] :as claims}]
|
||||
|
@ -116,13 +108,7 @@
|
|||
;; the user clicking the link he already has access to the
|
||||
;; email account.
|
||||
(with-meta claims
|
||||
{:transform-response
|
||||
(fn [request response]
|
||||
(let [uagent (get-in request [:headers "user-agent"])
|
||||
id (session/create! session {:profile-id member-id
|
||||
:user-agent uagent})]
|
||||
(assoc response
|
||||
:cookies (session/cookies session {:value id}))))})))
|
||||
{:transform-response ((:create session) member-id)})))
|
||||
|
||||
;; In this case, we wait until frontend app redirect user to
|
||||
;; registeration page, the user is correctly registered and the
|
||||
|
|
|
@ -191,3 +191,94 @@
|
|||
;; TODO: profile deletion with owner teams
|
||||
;; TODO: profile registration
|
||||
;; TODO: profile password recovery
|
||||
|
||||
(t/deftest test-register-when-registration-disabled
|
||||
(with-mocks [mock {:target 'app.config/get
|
||||
:return (th/mock-config-get-with
|
||||
{:registration-enabled false})}]
|
||||
(let [data {::th/type :register-profile
|
||||
:email "user@example.com"
|
||||
:password "foobar"
|
||||
:fullname "foobar"}
|
||||
out (th/mutation! data)
|
||||
error (:error out)
|
||||
edata (ex-data error)]
|
||||
(t/is (th/ex-info? error))
|
||||
(t/is (= (:type edata) :restriction))
|
||||
(t/is (= (:code edata) :registration-disabled)))))
|
||||
|
||||
(t/deftest test-register-existing-profile
|
||||
(let [profile (th/create-profile* 1)
|
||||
data {::th/type :register-profile
|
||||
:email (:email profile)
|
||||
:password "foobar"
|
||||
:fullname "foobar"}
|
||||
out (th/mutation! data)
|
||||
error (:error out)
|
||||
edata (ex-data error)]
|
||||
(t/is (th/ex-info? error))
|
||||
(t/is (= (:type edata) :validation))
|
||||
(t/is (= (:code edata) :email-already-exists))))
|
||||
|
||||
(t/deftest test-register-profile
|
||||
(with-mocks [mock {:target 'app.emails/send!
|
||||
:return nil}]
|
||||
(let [pool (:app.db/pool th/*system*)
|
||||
data {::th/type :register-profile
|
||||
:email "user@example.com"
|
||||
:password "foobar"
|
||||
:fullname "foobar"}
|
||||
out (th/mutation! data)]
|
||||
;; (th/print-result! out)
|
||||
(let [mock (deref mock)
|
||||
[_ _ params] (:call-args mock)]
|
||||
;; (clojure.pprint/pprint params)
|
||||
(t/is (:called? mock))
|
||||
(t/is (= (:email data) (:to params)))
|
||||
(t/is (contains? params :extra-data))
|
||||
(t/is (contains? params :token)))
|
||||
|
||||
(let [result (:result out)]
|
||||
(t/is (false? (:is-demo result)))
|
||||
(t/is (= (:email data) (:email result)))
|
||||
(t/is (= "penpot" (:auth-backend result)))
|
||||
(t/is (= "foobar" (:fullname result)))
|
||||
(t/is (not (contains? result :password)))))))
|
||||
|
||||
(t/deftest test-register-profile-with-bounced-email
|
||||
(with-mocks [mock {:target 'app.emails/send!
|
||||
:return nil}]
|
||||
(let [pool (:app.db/pool th/*system*)
|
||||
data {::th/type :register-profile
|
||||
:email "user@example.com"
|
||||
:password "foobar"
|
||||
:fullname "foobar"}
|
||||
_ (th/create-global-complaint-for pool {:type :bounce :email "user@example.com"})
|
||||
out (th/mutation! data)]
|
||||
;; (th/print-result! out)
|
||||
|
||||
(let [mock (deref mock)]
|
||||
(t/is (false? (:called? mock))))
|
||||
|
||||
(let [error (:error out)
|
||||
edata (ex-data error)]
|
||||
(t/is (th/ex-info? error))
|
||||
(t/is (= (:type edata) :validation))
|
||||
(t/is (= (:code edata) :email-has-permanent-bounces))))))
|
||||
|
||||
(t/deftest test-register-profile-with-complained-email
|
||||
(with-mocks [mock {:target 'app.emails/send!
|
||||
:return nil}]
|
||||
(let [pool (:app.db/pool th/*system*)
|
||||
data {::th/type :register-profile
|
||||
:email "user@example.com"
|
||||
:password "foobar"
|
||||
:fullname "foobar"}
|
||||
_ (th/create-global-complaint-for pool {:type :complaint :email "user@example.com"})
|
||||
out (th/mutation! data)]
|
||||
|
||||
(let [mock (deref mock)]
|
||||
(t/is (true? (:called? mock))))
|
||||
|
||||
(let [result (:result out)]
|
||||
(t/is (= (:email data) (:email result)))))))
|
||||
|
|
|
@ -122,18 +122,6 @@
|
|||
(seq params))
|
||||
(send-mutation! id form)))
|
||||
|
||||
(defmethod mutation :login
|
||||
[id params]
|
||||
(let [uri (str cfg/public-uri "/api/login")]
|
||||
(->> (http/send! {:method :post :uri uri :body params})
|
||||
(rx/mapcat handle-response))))
|
||||
|
||||
(defmethod mutation :logout
|
||||
[id params]
|
||||
(let [uri (str cfg/public-uri "/api/logout")]
|
||||
(->> (http/send! {:method :post :uri uri :body params})
|
||||
(rx/mapcat handle-response))))
|
||||
|
||||
(defmethod mutation :login-with-ldap
|
||||
[id params]
|
||||
(let [uri (str cfg/public-uri "/api/login-ldap")]
|
||||
|
|
|
@ -63,7 +63,6 @@
|
|||
|
||||
on-error
|
||||
(fn [form event]
|
||||
(js/console.log error?)
|
||||
(reset! error? true))
|
||||
|
||||
on-submit
|
||||
|
@ -107,8 +106,7 @@
|
|||
:help-icon i/eye
|
||||
:label (tr "auth.password")}]]
|
||||
[:& fm/submit-button
|
||||
{:label (tr "auth.login-submit")
|
||||
:on-click on-submit}]
|
||||
{:label (tr "auth.login-submit")}]
|
||||
|
||||
(when cfg/login-with-ldap
|
||||
[:& fm/submit-button
|
||||
|
|
Loading…
Add table
Reference in a new issue