0
Fork 0
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:
Andrey Antukh 2020-08-10 10:52:45 +02:00
commit b0fa8c3bfc
12 changed files with 186 additions and 6 deletions

View file

@ -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"}

View file

@ -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]

View file

@ -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}]

View 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}))))

View file

@ -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 ##

View file

@ -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);

View file

@ -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" : {

View file

@ -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")))

View file

@ -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

View file

@ -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?)

View file

@ -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}]

View file

@ -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"}]))