mirror of
https://github.com/penpot/penpot.git
synced 2026-05-10 18:48:23 +00:00
The malli schema for the LDAP provider params (`schema:params` in `backend/src/app/auth/ldap.clj`) declared the bind-password slot as `:bind-passwor` (missing trailing `d`). The runtime code in the same file uses `:bind-password` everywhere — `prepare-params` reads `(:bind-password cfg)` on line 21 and `try-connectivity` reads `(:bind-password cfg)` on line 89. Effects of the typo: 1. The schema slot for `:bind-password` is missing, so a wrong type (e.g. a number or vector instead of a string) for the actual key slips through `check-params` unvalidated. Malli `[:map ...]` is open by default, so the genuine `:bind-password` key is silently accepted as an unknown extra key. 2. Anyone reading the schema (operator, future contributor, or tooling generating docs) sees a non-existent `:bind-passwor` parameter and could legitimately set that key — schema would accept it, runtime would never read it, LDAP bind would silently fail with a confusing "no password" error. Cross-checked against the pre-malli `clojure.spec` shape removed in commit 88fb5e7ab (2024-10-29, "♻️ Update integrant to latest version", which carried the spec→malli migration). The deleted spec defined `(s/def ::bind-password ::us/string)` correctly — the typo was introduced when re-typing the keys into the new malli vector-of- tuples form. Add a CHANGES.md entry under the 2.17.0 Unreleased 🐛 Bugs fixed section. One-character fix. Signed-off-by: Andrey Antukh <niwi@niwi.nz> Co-authored-by: Andrey Antukh <niwi@niwi.nz>
137 lines
4.1 KiB
Clojure
137 lines
4.1 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) KALEIDOS INC
|
|
|
|
(ns app.auth.ldap
|
|
(:require
|
|
[app.common.exceptions :as ex]
|
|
[app.common.logging :as l]
|
|
[app.common.schema :as sm]
|
|
[clj-ldap.client :as ldap]
|
|
[clojure.string]
|
|
[integrant.core :as ig]))
|
|
|
|
(defn- prepare-params
|
|
[cfg]
|
|
{:ssl? (:ssl cfg)
|
|
:startTLS? (:tls cfg)
|
|
:bind-dn (:bind-dn cfg)
|
|
:password (:bind-password cfg)
|
|
:host {:address (:host cfg)
|
|
:port (:port cfg)}})
|
|
|
|
(defn- connect
|
|
"Connects to the LDAP provider and returns a connection. An
|
|
exception is raised if no connection is possible."
|
|
^java.lang.AutoCloseable
|
|
[cfg]
|
|
(try
|
|
(-> cfg prepare-params ldap/connect)
|
|
(catch Throwable cause
|
|
(ex/raise :type :restriction
|
|
:code :unable-to-connect-to-ldap
|
|
:hint "unable to connect to ldap server"
|
|
:cause cause))))
|
|
|
|
(defn- replace-several [s & {:as replacements}]
|
|
(reduce-kv clojure.string/replace s replacements))
|
|
|
|
(defn- search-user
|
|
[{:keys [::conn base-dn] :as cfg} email]
|
|
(let [query (replace-several (:query cfg) ":username" email)
|
|
attrs [(:attrs-username cfg)
|
|
(:attrs-email cfg)
|
|
(:attrs-fullname cfg)]
|
|
params {:filter query
|
|
:sizelimit 1
|
|
:attributes attrs}]
|
|
(first (ldap/search conn base-dn params))))
|
|
|
|
(defn- retrieve-user
|
|
[{:keys [::conn] :as cfg} {:keys [email password]}]
|
|
(when-let [{:keys [dn] :as user} (search-user cfg email)]
|
|
(when (ldap/bind? conn dn password)
|
|
{:fullname (get user (-> cfg :attrs-fullname keyword))
|
|
:email email
|
|
:backend "ldap"})))
|
|
|
|
(def ^:private schema:info-data
|
|
[:map
|
|
[:fullname ::sm/text]
|
|
[:email ::sm/email]
|
|
[:backend ::sm/text]])
|
|
|
|
(def ^:private valid-info-data?
|
|
(sm/lazy-validator schema:info-data))
|
|
|
|
(def ^:private explain-info-data
|
|
(sm/lazy-explainer schema:info-data))
|
|
|
|
(defn authenticate
|
|
[cfg params]
|
|
(with-open [conn (connect cfg)]
|
|
(when-let [user (-> (assoc cfg ::conn conn)
|
|
(retrieve-user params))]
|
|
(when-not (valid-info-data? user)
|
|
(let [explain (explain-info-data user)]
|
|
(l/warn :hint "invalid response from ldap, looks like ldap is not configured correctly" :data user)
|
|
(ex/raise :type :restriction
|
|
:code :wrong-ldap-response
|
|
:explain explain)))
|
|
user)))
|
|
|
|
(defn- try-connectivity
|
|
[cfg]
|
|
;; If we have ldap parameters, try to establish connection
|
|
(when (and (:bind-dn cfg)
|
|
(:bind-password cfg)
|
|
(:host cfg)
|
|
(:port cfg))
|
|
(try
|
|
(with-open [_ (connect cfg)]
|
|
(l/info :hint "provider initialized"
|
|
:provider "ldap"
|
|
:host (:host cfg)
|
|
:port (:port cfg)
|
|
:tls? (:tls cfg)
|
|
:ssl? (:ssl cfg)
|
|
:bind-dn (:bind-dn cfg)
|
|
:base-dn (:base-dn cfg)
|
|
:query (:query cfg))
|
|
cfg)
|
|
(catch Throwable cause
|
|
(l/error :hint "unable to connect to LDAP server (LDAP auth provider disabled)"
|
|
:host (:host cfg) :port (:port cfg) :cause cause)
|
|
nil))))
|
|
|
|
(def ^:private schema:params
|
|
[:map
|
|
[:host {:optional true} :string]
|
|
[:port {:optional true} ::sm/int]
|
|
[:bind-dn {:optional true} :string]
|
|
[:bind-password {:optional true} :string]
|
|
[:query {:optional true} :string]
|
|
[:base-dn {:optional true} :string]
|
|
[:attrs-email {:optional true} :string]
|
|
[:attrs-username {:optional true} :string]
|
|
[:attrs-fullname {:optional true} :string]
|
|
[:ssl {:optional true} ::sm/boolean]
|
|
[:tls {:optional true} ::sm/boolean]])
|
|
|
|
(def ^:private check-params
|
|
(sm/check-fn schema:params :hint "Invalid LDAP provider parameters"))
|
|
|
|
(defmethod ig/assert-key ::provider
|
|
[_ params]
|
|
(when (:enabled params)
|
|
(some->> params check-params)))
|
|
|
|
(defmethod ig/init-key ::provider
|
|
[_ cfg]
|
|
(when (:enabled cfg)
|
|
(try-connectivity cfg)))
|
|
|
|
(sm/register! ::provider schema:params)
|