mirror of
https://github.com/penpot/penpot.git
synced 2025-03-13 00:01:51 -05:00
Merge branch 'other/ldap' into develop
This commit is contained in:
commit
b0fa8c3bfc
12 changed files with 186 additions and 6 deletions
|
@ -53,6 +53,8 @@
|
|||
com.draines/postal {:mvn/version "2.0.3"
|
||||
:exclusions [commons-codec/commons-codec]}
|
||||
|
||||
puppetlabs/clj-ldap {:mvn/version"0.3.0"}
|
||||
|
||||
;; exception printing
|
||||
io.aviso/pretty {:mvn/version "0.1.37"}
|
||||
|
||||
|
|
|
@ -44,7 +44,20 @@
|
|||
:registration-enabled true
|
||||
:registration-domain-whitelist ""
|
||||
:debug-humanize-transit true
|
||||
})
|
||||
|
||||
;; LDAP auth disabled by default. Set ldap-auth-host to enable
|
||||
;:ldap-auth-host "ldap.mysupercompany.com"
|
||||
;:ldap-auth-port 389
|
||||
;:ldap-bind-dn "cn=admin,dc=ldap,dc=mysupercompany,dc=com"
|
||||
;:ldap-bind-password "verysecure"
|
||||
;:ldap-auth-ssl false
|
||||
;:ldap-auth-starttls false
|
||||
;:ldap-auth-base-dn "ou=People,dc=ldap,dc=mysupercompany,dc=com"
|
||||
:ldap-auth-user-query "(|(uid=$username)(mail=$username))"
|
||||
:ldap-auth-username-attribute "uid"
|
||||
:ldap-auth-email-attribute "mail"
|
||||
:ldap-auth-fullname-attribute "displayName"
|
||||
:ldap-auth-avatar-attribute "jpegPhoto"})
|
||||
|
||||
(s/def ::http-server-port ::us/integer)
|
||||
(s/def ::http-server-debug ::us/boolean)
|
||||
|
@ -78,6 +91,19 @@
|
|||
(s/def ::google-client-id ::us/string)
|
||||
(s/def ::google-client-secret ::us/string)
|
||||
|
||||
(s/def ::ldap-auth-host ::us/string)
|
||||
(s/def ::ldap-auth-port ::us/integer)
|
||||
(s/def ::ldap-bind-dn ::us/string)
|
||||
(s/def ::ldap-bind-password ::us/string)
|
||||
(s/def ::ldap-auth-ssl ::us/boolean)
|
||||
(s/def ::ldap-auth-starttls ::us/boolean)
|
||||
(s/def ::ldap-auth-base-dn ::us/string)
|
||||
(s/def ::ldap-auth-user-query ::us/string)
|
||||
(s/def ::ldap-auth-username-attribute ::us/string)
|
||||
(s/def ::ldap-auth-email-attribute ::us/string)
|
||||
(s/def ::ldap-auth-fullname-attribute ::us/string)
|
||||
(s/def ::ldap-auth-avatar-attribute ::us/string)
|
||||
|
||||
(s/def ::config
|
||||
(s/keys :opt-un [::http-server-cors
|
||||
::http-server-debug
|
||||
|
@ -106,7 +132,19 @@
|
|||
::allow-demo-users
|
||||
::registration-enabled
|
||||
::registration-domain-whitelist
|
||||
::image-process-max-threads]))
|
||||
::image-process-max-threads
|
||||
::ldap-auth-host
|
||||
::ldap-auth-port
|
||||
::ldap-bind-dn
|
||||
::ldap-bind-password
|
||||
::ldap-auth-ssl
|
||||
::ldap-auth-starttls
|
||||
::ldap-auth-base-dn
|
||||
::ldap-auth-user-query
|
||||
::ldap-auth-username-attribute
|
||||
::ldap-auth-email-attribute
|
||||
::ldap-auth-fullname-attribute
|
||||
::ldap-auth-avatar-attribute]))
|
||||
|
||||
(defn env->config
|
||||
[env]
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
[uxbox.http.handlers :as handlers]
|
||||
[uxbox.http.auth :as auth]
|
||||
[uxbox.http.auth.google :as google]
|
||||
[uxbox.http.auth.ldap :as ldap]
|
||||
[uxbox.http.middleware :as middleware]
|
||||
[uxbox.http.session :as session]
|
||||
[uxbox.http.ws :as ws]
|
||||
|
@ -48,6 +49,8 @@
|
|||
:method :post}]
|
||||
["/logout" {:handler auth/logout-handler
|
||||
:method :post}]
|
||||
["/login-ldap" {:handler ldap/auth
|
||||
:method :post}]
|
||||
|
||||
["/w" {:middleware [session/auth]}
|
||||
["/query/:type" {:get handlers/query-handler}]
|
||||
|
|
69
backend/src/uxbox/http/auth/ldap.clj
Normal file
69
backend/src/uxbox/http/auth/ldap.clj
Normal file
|
@ -0,0 +1,69 @@
|
|||
(ns uxbox.http.auth.ldap
|
||||
(:require
|
||||
[clj-ldap.client :as client]
|
||||
[clojure.set :as set]
|
||||
[mount.core :refer [defstate]]
|
||||
[uxbox.common.exceptions :as ex]
|
||||
[uxbox.config :as cfg]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.http.session :as session]
|
||||
[clojure.tools.logging :as log]))
|
||||
|
||||
|
||||
(defn replace-several [s & {:as replacements}]
|
||||
(reduce-kv clojure.string/replace s replacements))
|
||||
|
||||
(defstate *ldap-pool
|
||||
:start (delay
|
||||
(try
|
||||
(client/connect (merge {:host {:address (:ldap-auth-host cfg/config)
|
||||
:port (:ldap-auth-port cfg/config)}}
|
||||
(-> cfg/config
|
||||
(select-keys [:ldap-auth-ssl
|
||||
:ldap-auth-starttls
|
||||
:ldap-bind-dn
|
||||
:ldap-bind-password])
|
||||
(set/rename-keys {:ldap-auth-ssl :ssl?
|
||||
:ldap-auth-starttls :startTLS?
|
||||
:ldap-bind-dn :bind-dn
|
||||
:ldap-bind-password :password}))))
|
||||
(catch Exception e
|
||||
(log/errorf e "Cannot connect to LDAP %s:%s"
|
||||
(:ldap-auth-host cfg/config) (:ldap-auth-port cfg/config)))))
|
||||
:stop (when (realized? *ldap-pool)
|
||||
(some-> *ldap-pool deref (.close))))
|
||||
|
||||
(defn- auth-with-ldap [username password]
|
||||
(when-some [conn (some-> *ldap-pool deref)]
|
||||
(let [user-search-query (replace-several (:ldap-auth-user-query cfg/config)
|
||||
"$username" username)
|
||||
user-attributes (-> cfg/config
|
||||
(select-keys [:ldap-auth-username-attribute
|
||||
:ldap-auth-email-attribute
|
||||
:ldap-auth-fullname-attribute
|
||||
:ldap-auth-avatar-attribute])
|
||||
vals)]
|
||||
(when-some [user-entry (-> conn
|
||||
(client/search (:ldap-auth-base-dn cfg/config)
|
||||
{:filter user-search-query
|
||||
:sizelimit 1
|
||||
:attributes user-attributes})
|
||||
(first))]
|
||||
(when-not (client/bind? conn (:dn user-entry) password)
|
||||
(ex/raise :type :authentication
|
||||
:code ::wrong-credentials))
|
||||
(set/rename-keys user-entry {(keyword (:ldap-auth-avatar-attribute cfg/config)) :photo
|
||||
(keyword (:ldap-auth-fullname-attribute cfg/config)) :fullname
|
||||
(keyword (:ldap-auth-email-attribute cfg/config)) :email})))))
|
||||
|
||||
(defn auth [req]
|
||||
(let [data (:body-params req)
|
||||
uagent (get-in req [:headers "user-agent"])]
|
||||
(when-some [info (auth-with-ldap (:email data) (:password data))]
|
||||
(let [profile (sm/handle {::sm/type :login-or-register
|
||||
:email (:email info)
|
||||
:fullname (:fullname info)})
|
||||
sid (session/create (:id profile) uagent)]
|
||||
{:status 200
|
||||
:cookies (session/cookies sid)
|
||||
:body profile}))))
|
|
@ -7,11 +7,13 @@
|
|||
**Only available at build time!**
|
||||
|
||||
- `-e UXBOX_PUBLIC_URI=...` (defaults to `http://localhost:6060`)
|
||||
- `-e UXBOX_GOOGLE_CLIENT_ID=...` (defaults to `true`)
|
||||
- `-e UXBOX_LOGIN_WITH_LDAP=...` (defaults to `false`)
|
||||
- `-e UXBOX_DEMO_WARNING=...` (defaults to `true`)
|
||||
|
||||
## Backend configuration parameters ##
|
||||
|
||||
Backend accepts a bunch of configuration parameters (detailed abowe),
|
||||
Backend accepts a bunch of configuration parameters (detailed above),
|
||||
that can be passed in different ways. The preferred one is using
|
||||
environment variables.
|
||||
|
||||
|
@ -41,6 +43,19 @@ respective defaults):
|
|||
- `UXBOX_REGISTRATION_DOMAIN_WHITELIST=""` (comma-separated domains, defaults to `""` which means that all domains are allowed)
|
||||
- `UXBOX_DEBUG_HUMANIZE_TRANSIT=true`
|
||||
|
||||
- `UXBOX_LDAP_AUTH_HOST=` (default undefined)
|
||||
- `UXBOX_LDAP_AUTH_PORT=` (default undefined)
|
||||
- `UXBOX_LDAP_AUTH_VERSION=3`
|
||||
- `UXBOX_LDAP_BIND_DN=` (default undefined)
|
||||
- `UXBOX_LDAP_BIND_PASSWORD=` (default undefined)
|
||||
- `UXBOX_LDAP_AUTH_SSL=` (default `false`)
|
||||
- `UXBOX_LDAP_AUTH_STARTTLS=` (default `false`)
|
||||
- `UXBOX_LDAP_AUTH_BASE_DN=` (default undefined)
|
||||
- `UXBOX_LDAP_AUTH_USER_QUERY=(|(uid=$username)(mail=$username))`
|
||||
- `UXBOX_LDAP_AUTH_USERNAME_ATTRIBUTE=uid`
|
||||
- `UXBOX_LDAP_AUTH_EMAIL_ATTRIBUTE=mail`
|
||||
- `UXBOX_LDAP_AUTH_FULLNAME_ATTRIBUTE=displayName`
|
||||
- `UXBOX_LDAP_AUTH_AVATAR_ATTRIBUTE=jpegPhoto`
|
||||
|
||||
## REPL ##
|
||||
|
||||
|
|
|
@ -113,6 +113,7 @@ function readConfig(data) {
|
|||
const demoWarn = process.env.UXBOX_DEMO_WARNING;
|
||||
const deployDate = process.env.UXBOX_DEPLOY_DATE;
|
||||
const deployCommit = process.env.UXBOX_DEPLOY_COMMIT;
|
||||
const loginWithLDAP = process.env.UXBOX_LOGIN_WITH_LDAP;
|
||||
|
||||
let cfg = {
|
||||
demoWarning: demoWarn === "true"
|
||||
|
@ -130,6 +131,10 @@ function readConfig(data) {
|
|||
cfg.deployCommit = deployCommit;
|
||||
}
|
||||
|
||||
if (loginWithLDAP !== undefined) {
|
||||
cfg.loginWithLDAP = loginWithLDAP;
|
||||
}
|
||||
|
||||
Object.assign(cfg, data);
|
||||
|
||||
return JSON.stringify(cfg);
|
||||
|
|
|
@ -107,6 +107,15 @@
|
|||
"es" : "Entrar"
|
||||
}
|
||||
},
|
||||
"auth.login-with-ldap-submit-label" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/login.cljs:108" ],
|
||||
"translations" : {
|
||||
"en" : "Sign in with LDAP",
|
||||
"fr" : "Se connecter via LDAP",
|
||||
"es" : "Entrar con LDAP",
|
||||
"ru" : "Вход через LDAP"
|
||||
}
|
||||
},
|
||||
"auth.login-subtitle" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/login.cljs:89" ],
|
||||
"translations" : {
|
||||
|
|
|
@ -15,10 +15,12 @@
|
|||
puri (obj/get config "publicURI")
|
||||
wuri (obj/get config "workerURI")
|
||||
gcid (obj/get config "googleClientID" true)
|
||||
lwl (obj/get config "loginWithLDAP" false)
|
||||
warn (obj/get config "demoWarning" true)]
|
||||
(def default-language "en")
|
||||
(def demo-warning warn)
|
||||
(def google-client-id gcid)
|
||||
(def login-with-ldap lwl)
|
||||
(def worker-uri wuri)
|
||||
(def public-uri puri)
|
||||
(def default-theme "default")))
|
||||
|
|
|
@ -78,6 +78,30 @@
|
|||
(rx/of (du/profile-fetched profile)
|
||||
(rt/nav' :dashboard-team {:team-id team-id}))))))
|
||||
|
||||
(defn login-with-ldap
|
||||
[{:keys [email password] :as data}]
|
||||
(us/verify ::login-params data)
|
||||
(ptk/reify ::login-with-ldap
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(merge state (dissoc initial-state :route :router)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [this state s]
|
||||
(let [{:keys [on-error on-success]
|
||||
:or {on-error identity
|
||||
on-success identity}} (meta data)
|
||||
params {:email email
|
||||
:password password
|
||||
:scope "webapp"}]
|
||||
(->> (rx/timer 100)
|
||||
(rx/mapcat #(rp/mutation :login-with-ldap params))
|
||||
(rx/tap on-success)
|
||||
(rx/catch (fn [err]
|
||||
(on-error err)
|
||||
(rx/empty)))
|
||||
(rx/map logged-in))))))
|
||||
|
||||
;; --- Logout
|
||||
|
||||
(def clear-user-data
|
||||
|
|
|
@ -104,5 +104,11 @@
|
|||
(->> (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")]
|
||||
(->> (http/send! {:method :post :uri uri :body params})
|
||||
(rx/mapcat handle-response))))
|
||||
|
||||
(def client-error? http/client-error?)
|
||||
(def server-error? http/server-error?)
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
(mf/defc login-form
|
||||
[{:keys [locale] :as props}]
|
||||
(let [error? (mf/use-state false)
|
||||
submit-event (mf/use-var da/login)
|
||||
|
||||
on-error
|
||||
(fn [form event]
|
||||
|
@ -53,7 +54,7 @@
|
|||
(reset! error? false)
|
||||
(let [params (with-meta (:clean-data form)
|
||||
{:on-error on-error})]
|
||||
(st/emit! (da/login params))))]
|
||||
(st/emit! (@submit-event params))))]
|
||||
|
||||
[:*
|
||||
(when @error?
|
||||
|
@ -78,7 +79,12 @@
|
|||
:help-icon i/eye
|
||||
:label (t locale "auth.password-label")}]
|
||||
[:& submit-button
|
||||
{:label (t locale "auth.login-submit-label")}]]]))
|
||||
{:label (t locale "auth.login-submit-label")
|
||||
:on-click #(reset! submit-event da/login)}]
|
||||
(when cfg/login-with-ldap
|
||||
[:& submit-button
|
||||
{:label (t locale "auth.login-with-ldap-submit-label")
|
||||
:on-click #(reset! submit-event da/login-with-ldap)}])]]))
|
||||
|
||||
(mf/defc login-page
|
||||
[{:keys [locale] :as props}]
|
||||
|
|
|
@ -121,12 +121,13 @@
|
|||
i/arrow-slide]]]))
|
||||
|
||||
(mf/defc submit-button
|
||||
[{:keys [label form] :as props}]
|
||||
[{:keys [label form on-click] :as props}]
|
||||
(let [form (mf/use-ctx form-ctx)]
|
||||
[:input.btn-primary.btn-large
|
||||
{:name "submit"
|
||||
:class (when-not (:valid form) "btn-disabled")
|
||||
:disabled (not (:valid form))
|
||||
:on-click on-click
|
||||
:value label
|
||||
:type "submit"}]))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue