mirror of
https://github.com/penpot/penpot.git
synced 2025-01-23 23:18:48 -05: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-auth-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 ::http-server-port ::us/integer)
|
||||
(s/def ::http-session-cookie-name ::us/string)
|
||||
|
@ -190,6 +193,9 @@
|
|||
::oidc-token-uri
|
||||
::oidc-auth-uri
|
||||
::oidc-user-uri
|
||||
::oidc-scopes
|
||||
::oidc-roles-attr
|
||||
::oidc-roles
|
||||
::host
|
||||
::http-server-port
|
||||
::http-session-idle-max-age
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
[app.util.logging :as l]
|
||||
[app.util.time :as dt]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.set :as set]
|
||||
[clojure.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[integrant.core :as ig]
|
||||
[lambdaisland.uri :as u]))
|
||||
|
||||
|
@ -32,9 +34,7 @@
|
|||
(defn register-profile
|
||||
[{:keys [rpc] :as cfg} info]
|
||||
(let [method-fn (get-in rpc [:methods :mutation :login-or-register])
|
||||
profile (method-fn {:email (:email info)
|
||||
:backend (:backend info)
|
||||
:fullname (:fullname info)})]
|
||||
profile (method-fn info)]
|
||||
(cond-> profile
|
||||
(some? (:invitation-token info))
|
||||
(assoc :invitation-token (:invitation-token info)))))
|
||||
|
@ -60,7 +60,7 @@
|
|||
:redirect_uri (build-redirect-uri cfg)
|
||||
:response_type "code"
|
||||
:state state
|
||||
:scope (:scope provider)}
|
||||
:scope (str/join " " (:scopes provider []))}
|
||||
query (u/map->query-string params)]
|
||||
(-> (u/uri (:auth-uri provider))
|
||||
(assoc :query query)
|
||||
|
@ -98,10 +98,10 @@
|
|||
res (http/send! req)]
|
||||
|
||||
(when (= 200 (:status res))
|
||||
(let [data (json/read-str (:body res))]
|
||||
{:email (get data "email")
|
||||
:backend (:name provider)
|
||||
:fullname (get data "name")})))
|
||||
(let [{:keys [name] :as data} (json/read-str (:body res) :key-fn keyword)]
|
||||
(-> data
|
||||
(assoc :backend (:name provider))
|
||||
(assoc :fullname name)))))
|
||||
|
||||
(catch Exception e
|
||||
(l/error :hint "unexpected exception on retrieve-user-info"
|
||||
|
@ -109,7 +109,7 @@
|
|||
nil)))
|
||||
|
||||
(defn retrieve-info
|
||||
[{:keys [tokens] :as cfg} request]
|
||||
[{:keys [tokens provider] :as cfg} request]
|
||||
(let [state (get-in request [:params :state])
|
||||
state (tokens :verify {:token state :iss :oauth})
|
||||
info (some->> (get-in request [:params :code])
|
||||
|
@ -119,6 +119,23 @@
|
|||
(ex/raise :type :internal
|
||||
: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
|
||||
(some? (:invitation-token state))
|
||||
(assoc :invitation-token (:invitation-token state)))))
|
||||
|
@ -198,7 +215,10 @@
|
|||
:token-uri (cf/get :oidc-token-uri)
|
||||
:auth-uri (cf/get :oidc-auth-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"}]
|
||||
(if (and (string? (:base-uri opts))
|
||||
(string? (:client-id opts))
|
||||
|
@ -218,10 +238,9 @@
|
|||
[cfg]
|
||||
(let [opts {:client-id (cf/get :google-client-id)
|
||||
:client-secret (cf/get :google-client-secret)
|
||||
:scope (str "email profile "
|
||||
"https://www.googleapis.com/auth/userinfo.email "
|
||||
"https://www.googleapis.com/auth/userinfo.profile "
|
||||
"openid")
|
||||
:scopes #{"email" "profile" "openid"
|
||||
"https://www.googleapis.com/auth/userinfo.email"
|
||||
"https://www.googleapis.com/auth/userinfo.profile"}
|
||||
:auth-uri "https://accounts.google.com/o/oauth2/v2/auth"
|
||||
:token-uri "https://oauth2.googleapis.com/token"
|
||||
:user-uri "https://openidconnect.googleapis.com/v1/userinfo"
|
||||
|
@ -237,7 +256,8 @@
|
|||
[cfg]
|
||||
(let [opts {:client-id (cf/get :github-client-id)
|
||||
:client-secret (cf/get :github-client-secret)
|
||||
:scope "user:email"
|
||||
:scopes #{"read:user"
|
||||
"user:email"}
|
||||
:auth-uri "https://github.com/login/oauth/authorize"
|
||||
:token-uri "https://github.com/login/oauth/access_token"
|
||||
:user-uri "https://api.github.com/user"
|
||||
|
@ -256,7 +276,7 @@
|
|||
opts {:base-uri base
|
||||
:client-id (cf/get :gitlab-client-id)
|
||||
:client-secret (cf/get :gitlab-client-secret)
|
||||
:scope "read_user"
|
||||
:scopes #{"read_user"}
|
||||
:auth-uri (str base "/oauth/authorize")
|
||||
:token-uri (str base "/oauth/token")
|
||||
:user-uri (str base "/api/v4/user")
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.rpc.mutations.profile
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
|
@ -303,16 +304,35 @@
|
|||
|
||||
(defn login-or-register
|
||||
[{: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
|
||||
{:id (uuid/next)
|
||||
:fullname fullname
|
||||
:fullname (:fullname info)
|
||||
:email (str/lower email)
|
||||
:lang (info->lang info)
|
||||
:auth-backend backend
|
||||
:is-active true
|
||||
:password "!"
|
||||
:props (db/tjson (info->props info))
|
||||
: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]
|
||||
(let [profile (->> (create-profile conn params)
|
||||
(create-profile-relations conn))]
|
||||
|
@ -321,7 +341,9 @@
|
|||
|
||||
(let [profile (profile/retrieve-profile-data-by-email conn email)
|
||||
profile (if profile
|
||||
(profile/populate-additional-data conn profile)
|
||||
(->> profile
|
||||
(update-profile conn params)
|
||||
(profile/populate-additional-data conn))
|
||||
(register-profile conn params))]
|
||||
(profile/strip-private-attrs profile))))
|
||||
|
||||
|
@ -346,7 +368,6 @@
|
|||
(update-profile conn params)
|
||||
nil))
|
||||
|
||||
|
||||
;; --- Mutation: Update Password
|
||||
|
||||
(declare validate-password!)
|
||||
|
|
|
@ -133,6 +133,20 @@
|
|||
::s/invalid))]
|
||||
(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
|
||||
|
||||
(defn spec-assert*
|
||||
|
|
|
@ -29,8 +29,7 @@
|
|||
|
||||
(defn- parse-locale
|
||||
[locale]
|
||||
(let [locale (-> (.-language globals/navigator)
|
||||
(str/lower)
|
||||
(let [locale (-> (str/lower locale)
|
||||
(str/replace "-" "_"))]
|
||||
(cond-> [locale]
|
||||
(str/includes? locale "_")
|
||||
|
@ -66,11 +65,17 @@
|
|||
(set! translations data))
|
||||
|
||||
(defn set-locale!
|
||||
[lang]
|
||||
(if lang
|
||||
(do
|
||||
(swap! storage assoc ::locale lang)
|
||||
(reset! locale lang))
|
||||
[lname]
|
||||
(if lname
|
||||
(let [supported (into #{} (map :value supported-locales))
|
||||
lname (loop [locales (seq (parse-locale lname))]
|
||||
(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
|
||||
(swap! storage dissoc ::locale)
|
||||
(reset! locale (autodetect)))))
|
||||
|
|
Loading…
Add table
Reference in a new issue