mirror of
https://github.com/penpot/penpot.git
synced 2025-05-15 19:06:38 +02:00
✨ Add the ability to check roles to openid integration.
This commit is contained in:
parent
465a25145d
commit
2828ccda7f
5 changed files with 93 additions and 27 deletions
|
@ -111,6 +111,9 @@
|
||||||
(s/def ::oidc-token-uri ::us/string)
|
(s/def ::oidc-token-uri ::us/string)
|
||||||
(s/def ::oidc-auth-uri ::us/string)
|
(s/def ::oidc-auth-uri ::us/string)
|
||||||
(s/def ::oidc-user-uri ::us/string)
|
(s/def ::oidc-user-uri ::us/string)
|
||||||
|
(s/def ::oidc-scopes ::us/set-of-str)
|
||||||
|
(s/def ::oidc-roles ::us/set-of-str)
|
||||||
|
(s/def ::oidc-roles-attr ::us/keyword)
|
||||||
(s/def ::host ::us/string)
|
(s/def ::host ::us/string)
|
||||||
(s/def ::http-server-port ::us/integer)
|
(s/def ::http-server-port ::us/integer)
|
||||||
(s/def ::http-session-cookie-name ::us/string)
|
(s/def ::http-session-cookie-name ::us/string)
|
||||||
|
@ -190,6 +193,9 @@
|
||||||
::oidc-token-uri
|
::oidc-token-uri
|
||||||
::oidc-auth-uri
|
::oidc-auth-uri
|
||||||
::oidc-user-uri
|
::oidc-user-uri
|
||||||
|
::oidc-scopes
|
||||||
|
::oidc-roles-attr
|
||||||
|
::oidc-roles
|
||||||
::host
|
::host
|
||||||
::http-server-port
|
::http-server-port
|
||||||
::http-session-idle-max-age
|
::http-session-idle-max-age
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
[app.util.logging :as l]
|
[app.util.logging :as l]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[clojure.data.json :as json]
|
[clojure.data.json :as json]
|
||||||
|
[clojure.set :as set]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[integrant.core :as ig]
|
[integrant.core :as ig]
|
||||||
[lambdaisland.uri :as u]))
|
[lambdaisland.uri :as u]))
|
||||||
|
|
||||||
|
@ -32,9 +34,7 @@
|
||||||
(defn register-profile
|
(defn register-profile
|
||||||
[{:keys [rpc] :as cfg} info]
|
[{:keys [rpc] :as cfg} info]
|
||||||
(let [method-fn (get-in rpc [:methods :mutation :login-or-register])
|
(let [method-fn (get-in rpc [:methods :mutation :login-or-register])
|
||||||
profile (method-fn {:email (:email info)
|
profile (method-fn info)]
|
||||||
:backend (:backend info)
|
|
||||||
:fullname (:fullname info)})]
|
|
||||||
(cond-> profile
|
(cond-> profile
|
||||||
(some? (:invitation-token info))
|
(some? (:invitation-token info))
|
||||||
(assoc :invitation-token (:invitation-token info)))))
|
(assoc :invitation-token (:invitation-token info)))))
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
:redirect_uri (build-redirect-uri cfg)
|
:redirect_uri (build-redirect-uri cfg)
|
||||||
:response_type "code"
|
:response_type "code"
|
||||||
:state state
|
:state state
|
||||||
:scope (:scope provider)}
|
:scope (str/join " " (:scopes provider []))}
|
||||||
query (u/map->query-string params)]
|
query (u/map->query-string params)]
|
||||||
(-> (u/uri (:auth-uri provider))
|
(-> (u/uri (:auth-uri provider))
|
||||||
(assoc :query query)
|
(assoc :query query)
|
||||||
|
@ -98,10 +98,10 @@
|
||||||
res (http/send! req)]
|
res (http/send! req)]
|
||||||
|
|
||||||
(when (= 200 (:status res))
|
(when (= 200 (:status res))
|
||||||
(let [data (json/read-str (:body res))]
|
(let [{:keys [name] :as data} (json/read-str (:body res) :key-fn keyword)]
|
||||||
{:email (get data "email")
|
(-> data
|
||||||
:backend (:name provider)
|
(assoc :backend (:name provider))
|
||||||
:fullname (get data "name")})))
|
(assoc :fullname name)))))
|
||||||
|
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(l/error :hint "unexpected exception on retrieve-user-info"
|
(l/error :hint "unexpected exception on retrieve-user-info"
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
nil)))
|
nil)))
|
||||||
|
|
||||||
(defn retrieve-info
|
(defn retrieve-info
|
||||||
[{:keys [tokens] :as cfg} request]
|
[{:keys [tokens provider] :as cfg} request]
|
||||||
(let [state (get-in request [:params :state])
|
(let [state (get-in request [:params :state])
|
||||||
state (tokens :verify {:token state :iss :oauth})
|
state (tokens :verify {:token state :iss :oauth})
|
||||||
info (some->> (get-in request [:params :code])
|
info (some->> (get-in request [:params :code])
|
||||||
|
@ -119,6 +119,23 @@
|
||||||
(ex/raise :type :internal
|
(ex/raise :type :internal
|
||||||
:code :unable-to-auth))
|
:code :unable-to-auth))
|
||||||
|
|
||||||
|
;; If the provider is OIDC, we can proceed to check
|
||||||
|
;; roles if they are defined.
|
||||||
|
(when (and (= "oidc" (:name provider))
|
||||||
|
(seq (:roles provider)))
|
||||||
|
(let [provider-roles (into #{} (:roles provider))
|
||||||
|
profile-roles (let [attr (cf/get :oidc-roles-attr :roles)
|
||||||
|
roles (get info attr)]
|
||||||
|
(cond
|
||||||
|
(string? roles) (into #{} (str/words roles))
|
||||||
|
(vector? roles) (into #{} roles)
|
||||||
|
:else #{}))]
|
||||||
|
;; check if profile has a configured set of roles
|
||||||
|
(when-not (set/subset? provider-roles profile-roles)
|
||||||
|
(ex/raise :type :internal
|
||||||
|
:code :unable-to-auth
|
||||||
|
:hint "not enought permissions"))))
|
||||||
|
|
||||||
(cond-> info
|
(cond-> info
|
||||||
(some? (:invitation-token state))
|
(some? (:invitation-token state))
|
||||||
(assoc :invitation-token (:invitation-token state)))))
|
(assoc :invitation-token (:invitation-token state)))))
|
||||||
|
@ -198,7 +215,10 @@
|
||||||
:token-uri (cf/get :oidc-token-uri)
|
:token-uri (cf/get :oidc-token-uri)
|
||||||
:auth-uri (cf/get :oidc-auth-uri)
|
:auth-uri (cf/get :oidc-auth-uri)
|
||||||
:user-uri (cf/get :oidc-user-uri)
|
:user-uri (cf/get :oidc-user-uri)
|
||||||
:scope "openid profile email name"
|
:scopes (into #{"openid" "profile" "email" "name"}
|
||||||
|
(cf/get :oidc-scopes #{}))
|
||||||
|
:roles-attr (cf/get :oidc-roles-attr)
|
||||||
|
:roles (cf/get :oidc-roles)
|
||||||
:name "oidc"}]
|
:name "oidc"}]
|
||||||
(if (and (string? (:base-uri opts))
|
(if (and (string? (:base-uri opts))
|
||||||
(string? (:client-id opts))
|
(string? (:client-id opts))
|
||||||
|
@ -218,10 +238,9 @@
|
||||||
[cfg]
|
[cfg]
|
||||||
(let [opts {:client-id (cf/get :google-client-id)
|
(let [opts {:client-id (cf/get :google-client-id)
|
||||||
:client-secret (cf/get :google-client-secret)
|
:client-secret (cf/get :google-client-secret)
|
||||||
:scope (str "email profile "
|
:scopes #{"email" "profile" "openid"
|
||||||
"https://www.googleapis.com/auth/userinfo.email "
|
"https://www.googleapis.com/auth/userinfo.email"
|
||||||
"https://www.googleapis.com/auth/userinfo.profile "
|
"https://www.googleapis.com/auth/userinfo.profile"}
|
||||||
"openid")
|
|
||||||
:auth-uri "https://accounts.google.com/o/oauth2/v2/auth"
|
:auth-uri "https://accounts.google.com/o/oauth2/v2/auth"
|
||||||
:token-uri "https://oauth2.googleapis.com/token"
|
:token-uri "https://oauth2.googleapis.com/token"
|
||||||
:user-uri "https://openidconnect.googleapis.com/v1/userinfo"
|
:user-uri "https://openidconnect.googleapis.com/v1/userinfo"
|
||||||
|
@ -237,7 +256,8 @@
|
||||||
[cfg]
|
[cfg]
|
||||||
(let [opts {:client-id (cf/get :github-client-id)
|
(let [opts {:client-id (cf/get :github-client-id)
|
||||||
:client-secret (cf/get :github-client-secret)
|
:client-secret (cf/get :github-client-secret)
|
||||||
:scope "user:email"
|
:scopes #{"read:user"
|
||||||
|
"user:email"}
|
||||||
:auth-uri "https://github.com/login/oauth/authorize"
|
:auth-uri "https://github.com/login/oauth/authorize"
|
||||||
:token-uri "https://github.com/login/oauth/access_token"
|
:token-uri "https://github.com/login/oauth/access_token"
|
||||||
:user-uri "https://api.github.com/user"
|
:user-uri "https://api.github.com/user"
|
||||||
|
@ -256,7 +276,7 @@
|
||||||
opts {:base-uri base
|
opts {:base-uri base
|
||||||
:client-id (cf/get :gitlab-client-id)
|
:client-id (cf/get :gitlab-client-id)
|
||||||
:client-secret (cf/get :gitlab-client-secret)
|
:client-secret (cf/get :gitlab-client-secret)
|
||||||
:scope "read_user"
|
:scopes #{"read_user"}
|
||||||
:auth-uri (str base "/oauth/authorize")
|
:auth-uri (str base "/oauth/authorize")
|
||||||
:token-uri (str base "/oauth/token")
|
:token-uri (str base "/oauth/token")
|
||||||
:user-uri (str base "/api/v4/user")
|
:user-uri (str base "/api/v4/user")
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
(ns app.rpc.mutations.profile
|
(ns app.rpc.mutations.profile
|
||||||
(:require
|
(:require
|
||||||
|
[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.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
@ -303,16 +304,35 @@
|
||||||
|
|
||||||
(defn login-or-register
|
(defn login-or-register
|
||||||
[{:keys [conn] :as cfg} {:keys [email backend] :as params}]
|
[{:keys [conn] :as cfg} {:keys [email backend] :as params}]
|
||||||
(letfn [(create-profile [conn {:keys [fullname email]}]
|
(letfn [(info->props [info]
|
||||||
|
(dissoc info :name :fullname :email :backend))
|
||||||
|
|
||||||
|
(info->lang [{:keys [locale] :as info}]
|
||||||
|
(when (and (string? locale)
|
||||||
|
(not (str/blank? locale)))
|
||||||
|
locale))
|
||||||
|
|
||||||
|
(create-profile [conn {:keys [email] :as info}]
|
||||||
(db/insert! conn :profile
|
(db/insert! conn :profile
|
||||||
{:id (uuid/next)
|
{:id (uuid/next)
|
||||||
:fullname fullname
|
:fullname (:fullname info)
|
||||||
:email (str/lower email)
|
:email (str/lower email)
|
||||||
|
:lang (info->lang info)
|
||||||
:auth-backend backend
|
:auth-backend backend
|
||||||
:is-active true
|
:is-active true
|
||||||
:password "!"
|
:password "!"
|
||||||
|
:props (db/tjson (info->props info))
|
||||||
:is-demo false}))
|
:is-demo false}))
|
||||||
|
|
||||||
|
(update-profile [conn info profile]
|
||||||
|
(let [props (d/merge (:props profile)
|
||||||
|
(info->props info))]
|
||||||
|
(db/update! conn :profile
|
||||||
|
{:props (db/tjson props)
|
||||||
|
:modified-at (dt/now)}
|
||||||
|
{:id (:id profile)})
|
||||||
|
(assoc profile :props props)))
|
||||||
|
|
||||||
(register-profile [conn params]
|
(register-profile [conn params]
|
||||||
(let [profile (->> (create-profile conn params)
|
(let [profile (->> (create-profile conn params)
|
||||||
(create-profile-relations conn))]
|
(create-profile-relations conn))]
|
||||||
|
@ -321,7 +341,9 @@
|
||||||
|
|
||||||
(let [profile (profile/retrieve-profile-data-by-email conn email)
|
(let [profile (profile/retrieve-profile-data-by-email conn email)
|
||||||
profile (if profile
|
profile (if profile
|
||||||
(profile/populate-additional-data conn profile)
|
(->> profile
|
||||||
|
(update-profile conn params)
|
||||||
|
(profile/populate-additional-data conn))
|
||||||
(register-profile conn params))]
|
(register-profile conn params))]
|
||||||
(profile/strip-private-attrs profile))))
|
(profile/strip-private-attrs profile))))
|
||||||
|
|
||||||
|
@ -346,7 +368,6 @@
|
||||||
(update-profile conn params)
|
(update-profile conn params)
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Update Password
|
;; --- Mutation: Update Password
|
||||||
|
|
||||||
(declare validate-password!)
|
(declare validate-password!)
|
||||||
|
|
|
@ -133,6 +133,20 @@
|
||||||
::s/invalid))]
|
::s/invalid))]
|
||||||
(s/def ::email (s/conformer cfn str)))
|
(s/def ::email (s/conformer cfn str)))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- SPEC: set-of-str
|
||||||
|
(letfn [(conformer [s]
|
||||||
|
(cond
|
||||||
|
(string? s) (into #{} (str/split s #"\s*,\s*"))
|
||||||
|
(set? s) (if (every? string? s)
|
||||||
|
s
|
||||||
|
::s/invalid)
|
||||||
|
:else ::s/invalid))
|
||||||
|
|
||||||
|
(unformer [s]
|
||||||
|
(str/join "," s))]
|
||||||
|
(s/def ::set-of-str (s/conformer conformer unformer)))
|
||||||
|
|
||||||
;; --- Macros
|
;; --- Macros
|
||||||
|
|
||||||
(defn spec-assert*
|
(defn spec-assert*
|
||||||
|
|
|
@ -29,8 +29,7 @@
|
||||||
|
|
||||||
(defn- parse-locale
|
(defn- parse-locale
|
||||||
[locale]
|
[locale]
|
||||||
(let [locale (-> (.-language globals/navigator)
|
(let [locale (-> (str/lower locale)
|
||||||
(str/lower)
|
|
||||||
(str/replace "-" "_"))]
|
(str/replace "-" "_"))]
|
||||||
(cond-> [locale]
|
(cond-> [locale]
|
||||||
(str/includes? locale "_")
|
(str/includes? locale "_")
|
||||||
|
@ -66,11 +65,17 @@
|
||||||
(set! translations data))
|
(set! translations data))
|
||||||
|
|
||||||
(defn set-locale!
|
(defn set-locale!
|
||||||
[lang]
|
[lname]
|
||||||
(if lang
|
(if lname
|
||||||
(do
|
(let [supported (into #{} (map :value supported-locales))
|
||||||
(swap! storage assoc ::locale lang)
|
lname (loop [locales (seq (parse-locale lname))]
|
||||||
(reset! locale lang))
|
(if-let [locale (first locales)]
|
||||||
|
(if (contains? supported locale)
|
||||||
|
locale
|
||||||
|
(recur (rest locales)))
|
||||||
|
cfg/default-language))]
|
||||||
|
(swap! storage assoc ::locale lname)
|
||||||
|
(reset! locale lname))
|
||||||
(do
|
(do
|
||||||
(swap! storage dissoc ::locale)
|
(swap! storage dissoc ::locale)
|
||||||
(reset! locale (autodetect)))))
|
(reset! locale (autodetect)))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue