diff --git a/backend/deps.edn b/backend/deps.edn index e544793a70..3af2bc1fcd 100644 --- a/backend/deps.edn +++ b/backend/deps.edn @@ -53,6 +53,8 @@ com.draines/postal {:mvn/version "2.0.3" :exclusions [commons-codec/commons-codec]} + org.clojars.pntblnk/clj-ldap {:mvn/version"0.0.16"} + ;; exception printing io.aviso/pretty {:mvn/version "0.1.37"} diff --git a/backend/src/uxbox/config.clj b/backend/src/uxbox/config.clj index 2299fb8d98..4827cc16a6 100644 --- a/backend/src/uxbox/config.clj +++ b/backend/src/uxbox/config.clj @@ -43,8 +43,22 @@ :allow-demo-users true :registration-enabled true :registration-domain-whitelist "" - :debug-humanize-transit true - }) + :debug-humanize-transit true}) + + ;; LDAP auth disabled by default + ;:ldap-auth-host "ldap.mysupercompany.com" + ;:ldap-auth-port 636 + ;:ldap-auth-version "3" + ;:ldap-bind-dn "cn=admin,dc=ldap,dc=mysupercompany,dc=com" + ;:ldap-bind-password "verysecure" + ;:ldap-auth-ssl false + ;:ldap-auth-starttls true + ;: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 +92,21 @@ (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-auth-version ::us/string) +(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 ::ldap-auth-isactivedirectory ::us/boolean) + (s/def ::config (s/keys :opt-un [::http-server-cors ::http-server-debug @@ -106,7 +135,21 @@ ::allow-demo-users ::registration-enabled ::registration-domain-whitelist - ::image-process-max-threads])) + ::image-process-max-threads + ::ldap-auth-host + ::ldap-auth-port + ::ldap-auth-version + ::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 + ::ldap-auth-isactivedirectory])) (defn env->config [env] diff --git a/backend/src/uxbox/http.clj b/backend/src/uxbox/http.clj index 5d9838c361..cc02c18d0a 100644 --- a/backend/src/uxbox/http.clj +++ b/backend/src/uxbox/http.clj @@ -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}] diff --git a/backend/src/uxbox/http/auth/ldap.clj b/backend/src/uxbox/http/auth/ldap.clj new file mode 100644 index 0000000000..2263c02a39 --- /dev/null +++ b/backend/src/uxbox/http/auth/ldap.clj @@ -0,0 +1,66 @@ +(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])) + + +(defn replace-several [s & {:as replacements}] + (reduce-kv clojure.string/replace s replacements)) + +(defstate ldap-pool + :start (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})))) + :stop (client/close ldap-pool)) + +(defn- auth-with-ldap [username password] + (let [conn (client/get-connection ldap-pool) + 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)] + (try + (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})) + (finally (client/release-connection ldap-pool conn))))) + +(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}))))