From cc87dc12f735c0831cc5f5d2711fb21ba4db8e79 Mon Sep 17 00:00:00 2001 From: bskinny Date: Sat, 3 Oct 2020 22:51:20 -0400 Subject: [PATCH 1/5] Introduce bind method based on @yogthos PR --- README.md | 49 +++++++- src/clj_ldap/client.clj | 53 ++++++-- test/clj_ldap/test/client.clj | 229 +++++++++++++++++----------------- 3 files changed, 200 insertions(+), 131 deletions(-) diff --git a/README.md b/README.md index 92501e3..80d54aa 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ clj-ldap is a thin layer on the [unboundid sdk](http://www.unboundid.com/products/ldap-sdk/) and allows clojure programs to talk to ldap servers. This library is available on [clojars.org](http://clojars.org/search?q=clj-ldap) ```clojure - :dependencies [[org.clojars.pntblnk/clj-ldap "0.0.16"]] + :dependencies [[org.clojars.pntblnk/clj-ldap "0.0.17"]] ``` # Example @@ -30,7 +30,7 @@ clj-ldap is a thin layer on the [unboundid sdk](http://www.unboundid.com/product ## connect [options] -Connects to an ldap server and returns a, thread safe, [LDAPConnectionPool](http://www.unboundid.com/products/ldap-sdk/docs/javadoc/com/unboundid/ldap/sdk/LDAPConnectionPool.html). +Connects to an ldap server and returns a thread safe [LDAPConnectionPool](http://www.unboundid.com/products/ldap-sdk/docs/javadoc/com/unboundid/ldap/sdk/LDAPConnectionPool.html). Options is a map with the following entries: :host Either a string in the form "address:port" @@ -83,32 +83,69 @@ a connection is expected. Using a pool in this manner aleviates the caller from release connections. It will still be necessary to get and release a connection if a single connection is needed to process a sequence of operations. See the following bind? example. -## bind? [connection bind-dn password] [connection-pool bind-dn password] +## bind [connection bind-dn password] [connection-pool bind-dn password] Usage: ```clojure - (ldap/bind? pool "cn=dude,ou=people,dc=example,dc=com" "somepass") + ;; Authenticate user with given dn but the pool retains the previous identity on the connection + (try + (ldap/bind pool "cn=dude,ou=people,dc=example,dc=com" "somepass") + (catch LDAPException e + (if (= 49 (.intValue (.getResultCode e))) + (prn "Password invalid") + (throw e)))) (let [conn (ldap/get-connection pool) user-dn "uid=user.1,ou=people,dc=example,dc=com" user-password "password"] (try + ;; Passed a connection, a successful authentication changes the authorization identity of the connection (when (ldap/bind? conn user-dn user-password) (ldap/modify conn user-dn {:replace {:description "On sabatical"}})) (finally (ldap/release-connection pool conn)))) ``` Performs a bind operation using the provided connection, bindDN and -password. Returns true if successful. +password. Returns true if successful and false otherwise. + +When an LDAP connection object is used as the connection argument the +bind? function will attempt to change the identity of that connection +to that of the provided DN. Subsequent operations on that connection +will be done using the bound identity. -If an LDAPConnectionPool object is passed as the connection argument +If an LDAP connection pool object is passed as the connection argument the bind attempt will have no side-effects, leaving the state of the underlying connections unchanged. +Throws a [LDAPException](http://www.unboundid.com/products/ldap-sdk/docs/javadoc/com/unboundid/ldap/sdk/LDAPException.html) on unsuccessful authentication or other error. + +## bind? [connection bind-dn password] [connection-pool bind-dn password] + +Usage: +```clojure + ;; Authenticate user with given dn but the pool retains the previous identity on the connection + (ldap/bind? pool "cn=dude,ou=people,dc=example,dc=com" "somepass") + + (let [conn (ldap/get-connection pool) + user-dn "uid=user.1,ou=people,dc=example,dc=com" + user-password "password"] + (try + ;; Passed a connection, a successful authentication changes the authorization identity of the connection + (when (ldap/bind? conn user-dn user-password) + (ldap/modify conn user-dn {:replace {:description "On sabatical"}})) + (finally (ldap/release-connection pool conn)))) +``` +Performs a bind operation using the provided connection, bindDN and +password. Returns true if successful and false otherwise. + When an LDAP connection object is used as the connection argument the bind? function will attempt to change the identity of that connection to that of the provided DN. Subsequent operations on that connection will be done using the bound identity. +If an LDAP connection pool object is passed as the connection argument +the bind attempt will have no side-effects, leaving the state of the +underlying connections unchanged. + ## get [connection dn] [connection dn attributes] If successful, returns a map containing the entry for the given DN. diff --git a/src/clj_ldap/client.clj b/src/clj_ldap/client.clj index ba93e1e..86c1578 100644 --- a/src/clj_ldap/client.clj +++ b/src/clj_ldap/client.clj @@ -28,7 +28,7 @@ Control StartTLSPostConnectProcessor CompareRequest - CompareResult]) + CompareResult BindResult]) (:import [com.unboundid.ldap.sdk.extensions PasswordModifyExtendedRequest PasswordModifyExtendedResult @@ -192,6 +192,17 @@ conn) :else (LDAPConnection. opt host ldap-port)))) +(defn- bind-based-on-connection + "Common bind approach for the api: + connection represents pool then authenticate and revert bind association on pool connection, or + connection is plain then authenticate and remain bound. + Note: There is a retainIdentity control (1.3.6.1.4.1.30221.2.5.3) which might also be useful option in the plain + connection context but since we make this the default behavior of pool binds it is likely unnecessary." + [connection bind-dn password] + (if (instance? LDAPConnectionPool connection) + (.bindAndRevertAuthentication connection bind-dn password nil) + (.bind connection bind-dn password))) + (defn- bind-request "Returns a BindRequest object" [{:keys [bind-dn password]}] @@ -524,23 +535,41 @@ [pool connection] (.releaseAndReAuthenticateConnection pool connection)) +(defn bind + "Performs a bind operation using the provided connection or pool, bindDN and + password. If the bind is unsuccessful LDAPException is thrown. Otherwise, a + map is returned with :code, :name, and optional :diagnostic-message keys. + The :diagnostic-message might contain password expiration warnings, for instance. + + When an LDAP connection object is used as the connection argument the + bind function will attempt to change the identity of that connection + to that of the provided DN. Subsequent operations on that connection + will be done using the bound identity. + + If an LDAP connection pool object is passed as the connection argument + the bind attempt will have no side-effects, leaving the state of the + underlying connections unchanged." + [connection bind-dn password] + (let [^BindResult r (bind-based-on-connection connection bind-dn password)] + (merge (ldap-result r) + (when-let [diagnostic-message (.getDiagnosticMessage r)] + {:diagnostic-message diagnostic-message})))) + (defn bind? "Performs a bind operation using the provided connection, bindDN and -password. Returns true if successful. + password. Returns true if successful and false otherwise. -When an LDAP connection object is used as the connection argument the -bind? function will attempt to change the identity of that connection -to that of the provided DN. Subsequent operations on that connection -will be done using the bound identity. + When an LDAP connection object is used as the connection argument the + bind? function will attempt to change the identity of that connection + to that of the provided DN. Subsequent operations on that connection + will be done using the bound identity. -If an LDAP connection pool object is passed as the connection argument -the bind attempt will have no side-effects, leaving the state of the -underlying connections unchanged." + If an LDAP connection pool object is passed as the connection argument + the bind attempt will have no side-effects, leaving the state of the + underlying connections unchanged." [connection bind-dn password] (try - (let [r (if (instance? LDAPConnectionPool connection) - (.bindAndRevertAuthentication connection bind-dn password nil) - (.bind connection bind-dn password))] + (let [r (bind-based-on-connection connection bind-dn password)] (= ResultCode/SUCCESS (.getResultCode r))) (catch Exception _ false))) diff --git a/test/clj_ldap/test/client.clj b/test/clj_ldap/test/client.clj index 20e8417..95ed0c2 100644 --- a/test/clj_ldap/test/client.clj +++ b/test/clj_ldap/test/client.clj @@ -2,7 +2,8 @@ "Automated tests for clj-ldap" (:require [clj-ldap.client :as ldap] [clj-ldap.test.server :as server]) - (:use clojure.test)) + (:use clojure.test) + (:import (com.unboundid.ldap.sdk LDAPException))) ;; Tests are run over a variety of connection types (LDAP and LDAPS for now) @@ -130,14 +131,24 @@ (use-fixtures :once test-server) (deftest test-get - (is (= (ldap/get *conn* (:dn person-a*)) - (assoc (:object person-a*) :dn (:dn person-a*)))) - (is (= (ldap/get *conn* (:dn person-b*)) - (assoc (:object person-b*) :dn (:dn person-b*)))) - (is (= (ldap/get *conn* (:dn person-a*) [:cn :sn]) - {:dn (:dn person-a*) + (is (= (assoc (:object person-a*) :dn (:dn person-a*)) + (ldap/get *conn* (:dn person-a*)))) + (is (= (assoc (:object person-b*) :dn (:dn person-b*)) + (ldap/get *conn* (:dn person-b*)))) + (is (= {:dn (:dn person-a*) :cn (-> person-a* :object :cn) - :sn (-> person-a* :object :sn)}))) + :sn (-> person-a* :object :sn)} + (ldap/get *conn* (:dn person-a*) [:cn :sn])))) + +(deftest some-bind + (is (= {:code 0, :name "success"} + (ldap/bind *conn* (:dn person-a*) "passa"))) + (is (thrown? LDAPException (ldap/bind *conn* (:dn person-a*) "notthepass"))) + (is (thrown? LDAPException (ldap/bind *conn* "cn=does,ou=not,cn=exist" "password")))) + +(deftest bad-bind? + (is (= false (ldap/bind? *conn* (:dn person-a*) "not-the-password"))) + (is (= false (ldap/bind? *conn* "cn=does,ou=not,cn=exist" "password")))) (deftest test-bind (if (> (-> *conn* @@ -148,164 +159,158 @@ _ (ldap/bind? *c* (:dn person-a*) "passa") a (ldap/who-am-i *c*) _ (ldap/release-connection *conn* *c*)] - (is (= [before a] - ["" (:dn person-a*)])))))) + (is (= ["" (:dn person-a*)] [before a])))))) (deftest test-add-delete - (is (= (ldap/add *conn* (:dn person-c*) (:object person-c*)) - success*)) - (is (= (ldap/get *conn* (:dn person-c*)) - (assoc (:object person-c*) :dn (:dn person-c*)))) - (is (= (ldap/delete *conn* (:dn person-c*)) - success*)) + (is (= success* (ldap/add *conn* (:dn person-c*) (:object person-c*)))) + (is (= (assoc (:object person-c*) :dn (:dn person-c*)) + (ldap/get *conn* (:dn person-c*)))) + (is (= success* (ldap/delete *conn* (:dn person-c*)))) (is (nil? (ldap/get *conn* (:dn person-c*)))) - (is (= (ldap/add *conn* (str "changeNumber=1234," base*) + (is (= success* + (ldap/add *conn* (str "changeNumber=1234," base*) {:objectClass ["changeLogEntry"] :changeNumber 1234 :targetDN base* - :changeType "modify"}) - success*)) - (is (= (:changeNumber (ldap/get *conn* (str "changeNumber=1234," base*))) - "1234")) - (is (= (ldap/delete *conn* (str "changeNumber=1234," base*) - {:pre-read [:objectClass]}) - {:code 0, :name "success", - :pre-read {:objectClass #{"top" "changeLogEntry"}}}))) + :changeType "modify"}))) + (is (= "1234" (:changeNumber (ldap/get *conn* (str "changeNumber=1234," base*))))) + (is (= {:code 0, :name "success", + :pre-read {:objectClass #{"top" "changeLogEntry"}}} + (ldap/delete *conn* (str "changeNumber=1234," base*) + {:pre-read [:objectClass]})))) (deftest test-delete-subtree - (is (= (ldap/add *conn* (:dn person-c*) (:object person-c*)) - success*)) - (is (= (ldap/delete *conn* base* {:delete-subtree true}) - success*)) + (is (= success* (ldap/add *conn* (:dn person-c*) (:object person-c*)))) + (is (= success* (ldap/delete *conn* base* {:delete-subtree true}))) (is (nil? (ldap/get *conn* base*)))) (deftest test-modify-add - (is (= (ldap/modify *conn* (:dn person-a*) + (is (= {:code 0, :name "success", + :pre-read {:objectClass #{"top" "person"}, :cn "testa"}, + :post-read {:l "Hollywood", :cn "testa"}} + (ldap/modify *conn* (:dn person-a*) {:add {:objectClass "organizationalPerson" :l "Hollywood"} :pre-read #{:objectClass :l :cn} - :post-read #{:l :cn}}) - {:code 0, :name "success", - :pre-read {:objectClass #{"top" "person"}, :cn "testa"}, - :post-read {:l "Hollywood", :cn "testa"}})) - (is (= (ldap/modify - *conn* (:dn person-b*) - {:add {:telephoneNumber ["0000000005" "0000000006"]}}) - success*)) + :post-read #{:l :cn}}))) + (is (= success* + (ldap/modify *conn* (:dn person-b*) + {:add {:telephoneNumber ["0000000005" "0000000006"]}}))) (let [new-a (ldap/get *conn* (:dn person-a*)) new-b (ldap/get *conn* (:dn person-b*)) obj-a (:object person-a*) obj-b (:object person-b*)] - (is (= (:objectClass new-a) - (conj (:objectClass obj-a) "organizationalPerson"))) - (is (= (:l new-a) "Hollywood")) - (is (= (set (:telephoneNumber new-b)) - (set (concat (:telephoneNumber obj-b) - ["0000000005" "0000000006"])))))) + (is (= (conj (:objectClass obj-a) "organizationalPerson") + (:objectClass new-a))) + (is (= "Hollywood" (:l new-a))) + (is (= (set (concat (:telephoneNumber obj-b) + ["0000000005" "0000000006"])) + (set (:telephoneNumber new-b)))))) (deftest test-modify-delete (let [b-phonenums (-> person-b* :object :telephoneNumber)] - (is (= (ldap/modify *conn* (:dn person-a*) - {:delete {:description :all}}) - success*)) - (is (= (ldap/modify *conn* (:dn person-b*) - {:delete {:telephoneNumber (first b-phonenums)}}) - success*)) - (is (= (ldap/get *conn* (:dn person-a*)) - (-> (:object person-a*) + (is (= success* + (ldap/modify *conn* (:dn person-a*) + {:delete {:description :all}}))) + (is (= success* + (ldap/modify *conn* (:dn person-b*) + {:delete {:telephoneNumber (first b-phonenums)}}))) + (is (= (-> (:object person-a*) (dissoc :description) - (assoc :dn (:dn person-a*))))) - (is (= (ldap/get *conn* (:dn person-b*)) - (-> (:object person-b*) + (assoc :dn (:dn person-a*))) + (ldap/get *conn* (:dn person-a*)))) + (is (= (-> (:object person-b*) (assoc :telephoneNumber (second b-phonenums)) - (assoc :dn (:dn person-b*))))))) + (assoc :dn (:dn person-b*))) + (ldap/get *conn* (:dn person-b*)))))) (deftest test-modify-replace (let [new-phonenums (-> person-b* :object :telephoneNumber) certificate-data (read-bytes-from-file "test-resources/cert.binary")] - (is (= (ldap/modify *conn* (:dn person-a*) - {:replace {:telephoneNumber new-phonenums}}) - success*)) - (is (= (ldap/get *conn* (:dn person-a*)) - (-> (:object person-a*) + (is (= success* + (ldap/modify *conn* (:dn person-a*) + {:replace {:telephoneNumber new-phonenums}}))) + (is (= (-> (:object person-a*) (assoc :telephoneNumber new-phonenums) - (assoc :dn (:dn person-a*))))) - (is (= (ldap/modify *conn* (:dn person-a*) + (assoc :dn (:dn person-a*))) + (ldap/get *conn* (:dn person-a*)))) + + (is (= success* + (ldap/modify *conn* (:dn person-a*) {:add {:objectclass ["inetOrgPerson" "organizationalPerson"] :userCertificate certificate-data}} - {:proxied-auth (str "dn:" (:dn person-a*))}) - success*)) - (is (= (seq (:userCertificate + {:proxied-auth (str "dn:" (:dn person-a*))}))) + (is (= (seq certificate-data) + (seq (:userCertificate (first (ldap/search *conn* (:dn person-a*) {:scope :base :filter "(objectclass=inetorgperson)" :attributes [:userCertificate] - :byte-valued [:userCertificate]})))) - (seq certificate-data))) - (is (= (seq (:userCertificate + :byte-valued [:userCertificate]})))))) + (is (= (seq certificate-data) + (seq (:userCertificate (first (ldap/search *conn* (:dn person-a*) {:scope :base - :byte-valued [:userCertificate]})))) - (seq certificate-data))) - (is (= (seq (:userCertificate (ldap/get *conn* (:dn person-a*) + :byte-valued [:userCertificate]})))))) + (is (= (seq certificate-data) + (seq (:userCertificate (ldap/get *conn* (:dn person-a*) [:userCertificate] - [:userCertificate]))) - (seq certificate-data))))) + [:userCertificate]))))))) (deftest test-modify-all (let [b (:object person-b*) b-phonenums (:telephoneNumber b)] - (is (= (ldap/modify *conn* (:dn person-b*) + (is (= success* + (ldap/modify *conn* (:dn person-b*) {:add {:telephoneNumber "0000000005"} :delete {:telephoneNumber (second b-phonenums)} - :replace {:description "desc x"}}) - success*)) + :replace {:description "desc x"}}))) (let [new-b (ldap/get *conn* (:dn person-b*))] - (is (= (set (:telephoneNumber new-b)) - (set [(first b-phonenums) "0000000005"]))) - (is (= (:description new-b) "desc x"))))) + (is (= (set [(first b-phonenums) "0000000005"]) + (set (:telephoneNumber new-b)))) + (is (= "desc x" (:description new-b)))))) (deftest test-search - (is (= (set (map :cn - (ldap/search *conn* base* {:attributes [:cn]}))) - (set [nil "testa" "testb" "Saul Hazledine"]))) - (is (= (set (map :cn + (is (= (set [nil "testa" "testb" "Saul Hazledine"]) + (set (map :cn + (ldap/search *conn* base* {:attributes [:cn]}))))) + (is (= (set ["testa" "testb"]) + (set (map :cn (ldap/search *conn* base* {:attributes [:cn] :filter "cn=test*" - :proxied-auth (str "dn:" (:dn person-a*))}))) - (set ["testa" "testb"]))) - (is (= (map :cn + :proxied-auth (str "dn:" (:dn person-a*))}))))) + (is (= '("Saul Hazledine" "testa" "testb") + (map :cn (ldap/search *conn* base* {:filter "cn=*" :server-sort {:is-critical true - :sort-keys [:cn :ascending]}})) - '("Saul Hazledine" "testa" "testb"))) - (is (= (count (map :cn - (ldap/search *conn* base* - {:attributes [:cn] :filter "cn=*" - :size-limit 2}))) - 2)) - (is (= (:description (map :cn - (ldap/search *conn* base* - {:attributes [:cn] - :filter "cn=István Orosz" - :types-only true}))) - nil)) - (is (= (set (map :description + :sort-keys [:cn :ascending]}})))) + + (is (= 2 (count (map :cn + (ldap/search *conn* base* + {:attributes [:cn] :filter "cn=*" + :size-limit 2}))))) + (is (= nil (:description (map :cn + (ldap/search *conn* base* + {:attributes [:cn] + :filter "cn=István Orosz" + :types-only true}))))) + (is (= (set ["István Orosz"]) + (set (map :description (ldap/search *conn* base* - {:filter "cn=testb" :types-only false}))) - (set ["István Orosz"]))) + {:filter "cn=testb" :types-only false}))))) (binding [*side-effects* #{}] (ldap/search! *conn* base* {:attributes [:cn :sn] :filter "cn=test*"} (fn [x] (set! *side-effects* (conj *side-effects* (dissoc x :dn))))) - (is (= *side-effects* - (set [{:cn "testa" :sn "a"} - {:cn "testb" :sn "b"}]))))) + (is (= (set [{:cn "testa" :sn "a"} + {:cn "testb" :sn "b"}]) + *side-effects*)))) + (deftest test-search-all (let [options {:attributes [:cn] :page-size 2} @@ -319,10 +324,8 @@ (->cn-set eager-results))))) (deftest test-compare? - (is (= (ldap/compare? *conn* (:dn person-b*) - :description "István Orosz") - true)) - (is (= (ldap/compare? *conn* (:dn person-a*) - :description "István Orosz" - {:proxied-auth (str "dn:" (:dn person-b*))}) - false))) + (is (= true (ldap/compare? *conn* (:dn person-b*) + :description "István Orosz"))) + (is (= false (ldap/compare? *conn* (:dn person-a*) + :description "István Orosz" + {:proxied-auth (str "dn:" (:dn person-b*))})))) From 1db1fd4da80b256cf576c6d862c641c69b4da73e Mon Sep 17 00:00:00 2001 From: bskinny Date: Sat, 3 Oct 2020 22:52:48 -0400 Subject: [PATCH 2/5] Bump unboundid sdk version from 4.x to 5.1.1 --- project.clj | 4 ++-- test-resources/server.keystore | Bin 1313 -> 2609 bytes test/clj_ldap/test/server.clj | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/project.clj b/project.clj index 955a1e8..677beb3 100644 --- a/project.clj +++ b/project.clj @@ -1,8 +1,8 @@ -(defproject org.clojars.pntblnk/clj-ldap "0.0.16" +(defproject org.clojars.pntblnk/clj-ldap "0.0.17" :description "Clojure ldap client." :url "https://github.com/pauldorman/clj-ldap" :dependencies [[org.clojure/clojure "1.8.0"] - [com.unboundid/unboundid-ldapsdk "4.0.4"]] + [com.unboundid/unboundid-ldapsdk "5.1.1"]] :profiles {:test {:dependencies [[lein-clojars "0.9.1"]]}} :aot [clj-ldap.client] :license {:name "Eclipse Public License - v 1.0" diff --git a/test-resources/server.keystore b/test-resources/server.keystore index b97425285ae4c6f1212c755747d5075252d15c91..9244a334a67378f0d4f007fc69222a62eb7fdcf5 100644 GIT binary patch literal 2609 zcmY+Ec{mh`8pa1>nK5ZZLq{0OzRlQ0gJj!Ma34Vg?jU41!2h?8p9=x` z4d1uPq^IF7qk;t`#dt2i_=6Hq!pa6{zXBuzyYn0_CfT!x+1TX}XmQ)+lEV_gi z4=trF5|L5LH|(e98{j;+uI`D2RcVzDaV3#rrxx@3E4Kr-D%9f>Pgg54bK=R~54iOR z{-|PqhL0H?*T))YGp*3_N2D~@9Ynd)Q(g-@)}+|}K|5Z-k*iqdB1mBKn?hO`B zu$8HnEGh**C!605|IC&KGc&@&iu+J6R-?VUX*DTsj=3I_Dv}@Q>Z9YDqPvx$0j1b8 zhOG)tfvqi?1jV=&9&!SUl|I#};mh=TWBhV!-Uwa%=9uSrHXT5FP^>eq^ie@sDui#h zsTC#g_=o;$)v(8R{bAboR8rRHT)p=yktK~!OEx*dbD|Z+Z#&tY4+gp;IM2x#Vop!& z;u3icBF$a$uW|l93(u;Z^E7!&Dqu|gu0BXnR!%^2S+a(8^)jp=6*gY>>l%7eD-S-L zg`b&X6WJT!;VD_I&8*8dbNN{N|jH(l5f0Xt4 zYqxV_z))RTdIL2;rNEAak&|Qd9*(1J>yGS7jiNtgA2n&6GB{n{exx$&8Y12H8X)N{ zo6eJO+5T;F?NBgwcPKUeyIEMSCp6L|=4GF6O^{wq>f5qM*Qew&FlD9XTN)a zWM(k7Nnr^iWZcZ~TS_7bJ$A{xrQBu~dr)yE6e>B_Hv^C;+{LtvPP}}(wqs@EeL|;H zU>!LmQv>naj$J;9?zjqgRFn$M%6aA(XkjRHCngTS{nK@IKYlAims>VM=nj6N9buUW zwYz-|H4F)5PyYQ+8a;%PhqH{Zz7!p( zCH3fsOdUlV_~}on?XR=?9_M)x8&CWmwK#-1Sff#N3PV}v9# z-)26f8A9T3RD$@Cf+8?L5Wow72i*O&6#$;U^bSJopW108Kk$^D&uuR_QXPTR(9l#y zs%fcd9%Iz*?-D1+F*)YdQYQT8*D1dvPJMBuOH6q$AuS%J!wj z(l6FV5`h3Ja`^q^t3#7j>xvrTl5ZmKn?}=jvxJpCxCy{x)dY!Z20ONHp;*Pa5j8RWS``-6F|hMZ$QV z5vt|=C)DJ{-1+GF)*k?x9k`lW{LD?`m5U&rBn}s{R}mrAmajb!TLpDR=Oew8KRz#{ z8LNA{Q>Kjr&@JW>BRMuWqUH49V0gLuVN7??%JlfQ%UV^cNxn1_=jShdA{q>COx5zSTmh`O*~vya_w%w3M^wtmfe04k;GC8_GJ@h%D6<-hpU%NvJI1 zZVkYpWEy$GEMZ~?oI?l81#n^{VzqQKf%w21wS|Z%HIER@fa0X3NdmD}$Lq!UPhX(7 z4_esmxWQ3P*1tU=IT}14B06vT!SO^t*eWOVN^O6rz=j>+px90Lg2&l-&e3`sdeLy1 zyZxuhf^q-DH5ch&;Ioc4#Rb@#HU<1W_sa%kXINY1kA7@(erNAY&i?d{bHO|G6Kn&X za@MY4RmK}EBd$B!On6Mo-ubjiQq0$hbfRK<#=XaU%5A!6y;sR#AH+4BY)6*F0iiFX z*gNb@6>Y1>DA7A9Cy$4R%Pt}VCH7k7rD`65S%Buc$f>8gaRM`rZO_Ei*xRzrJWp*= zbrN$oCxO08{@`|y7ZlZ_9!*N142|LFaY__wrclfmKaIabXg|9AnKRsPK3R2u2uN*a zS!+6*GvQ&yq#5z;tAF%;X{3CVu1mf+1YYua(`j<8I=u#lH3?Vc8Pn*g86(zBLWN|K z13;>}9=V%SkTjcRCsE->dyOOY?|*R&@Ydgr~7i5*q@A5JP}C*yVWGSbt*& z0QrfrI@@us)QneatG&<~ak0ZxM`a+tmH3Q)UFw>Gk~NwC{^Alz-geWLm5n7X_dn_O B&F26B literal 1313 zcmezO_TO6u1_mY|W&~sI;?$zD)FR#F)S?ogSa9yt#<@UxfemeXHYw=88T%-Z-jX6s|tosXBs)x{C&iBW}@Sb1uQEj@qD|gdhFDNGEZ6l!#CBXj?d`JHk)R=-0O1kwI{1B zJnMcWvpuyt_388;-`Qm)`mWz^ez~^AZ6iyd-R{W?KbpR3f zsBp+3H#E;@Wslvr{VTJ3e=d4$?fGx}cZCaCTSOFR{%5szv4k7&d*#Cdwt&G@?#_lqZ*BgaAh6_mN9C@Z^OyX*s!m6h{(bT@KHqM@&Bm_P=5fw~g_)JXpvX`F7=6s4ENsH;p?TSP z`Q>>q5e^Iyb_@|NVRqMw#N2|MR6|~%!9a7kg;_#Ui%Se84a7lQW?=!B%%arflKi4d zg>$ndv5 zxKXxLNHgfZ*Bre!Po94h+#Y1}-i6_?oPEQ#K($QQSO=xR-;uVx5x@2y{kqHG+v(Y% zIoBJl8)QxLKcyvX&z>2ubjkerFIcJtln(jtUU~iB_G8jNtizulTd_=(pXGP+x64Q6 z4l|jC2uF!cH2PNhd7XCRb*=ZGl$a&wUi4r*&Lqk(Jt3f9RGItCrb#=S7QJra{tzMa z738$<4;Qpvn{9jV^q$o*W;?=nR!&*J`4A~qeY>ti`adgV7ietQ z;we_B{K7{=OnCcwPNSX^%O2W@Rdt8d^lCgsws><*8k3XYB4vqGJxE%S6u&8`W)i~zH&RI zC7W+w_Gw+#>GhqPo$nEwN27q}MiJKyI~p_1x)f)3B`!BmZdws@#ypQk6YewSyh^7qkb>)Y32hi(TKu7 zJGM4{JzTMo^+v{2hMsrTQi~N0oXYWFp226{^gPVS;@s1>3Hzdj@KIwe{S5xB%*I}?Y&1u=jA)ukD7At7S$%I c&6B&V)1Wxzle3lTJe?WW^d5+Ry}kV;0K%UQ+W-In diff --git a/test/clj_ldap/test/server.clj b/test/clj_ldap/test/server.clj index df7aef1..227bf76 100644 --- a/test/clj_ldap/test/server.clj +++ b/test/clj_ldap/test/server.clj @@ -27,6 +27,9 @@ (.setFormatter fileHandler (MinimalLogFormatter. nil false false true)) fileHandler)) +;; Server's certificate, with alias "server-cert" generated using keytool like so: +;; keytool -genkey -keyalg RSA -alias server-cert -keystore selfsigned.jks -validity 3650 -keysize 2048 + (defn- start-ldap-server "Setup a server listening on available LDAP and LDAPS ports chosen at random" [] From 6d2797df85e297eab7aa93a3a97d19a16baf0928 Mon Sep 17 00:00:00 2001 From: bskinny Date: Thu, 22 Oct 2020 20:52:03 -0400 Subject: [PATCH 3/5] Correct some indentation --- src/clj_ldap/client.clj | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/clj_ldap/client.clj b/src/clj_ldap/client.clj index 86c1578..e60361c 100644 --- a/src/clj_ldap/client.clj +++ b/src/clj_ldap/client.clj @@ -93,7 +93,7 @@ adding the DN. We pass along the byte-valued collection to properly return binary data." ([byte-valued] - (entry-as-map byte-valued true)) + (entry-as-map byte-valued true)) ([byte-valued dn?] (fn [entry] (let [attrs (seq (.getAttributes entry))] @@ -109,10 +109,10 @@ (condp instance? control PreReadResponseControl (update-in m [:pre-read] merge ((entry-as-map [] false) - (.getEntry control))) + (.getEntry control))) PostReadResponseControl (update-in m [:post-read] merge ((entry-as-map [] false) - (.getEntry control))) + (.getEntry control))) m)) (defn- add-response-controls @@ -473,8 +473,8 @@ [(createServerSideSort server-sort)] []) proxied-auth-control (if (not-nil? proxied-auth) - [(ProxiedAuthorizationV2RequestControl. proxied-auth)] - [])] + [(ProxiedAuthorizationV2RequestControl. proxied-auth)] + [])] (merge original {:base base :scope (get-scope scope) :filter filter @@ -551,9 +551,9 @@ underlying connections unchanged." [connection bind-dn password] (let [^BindResult r (bind-based-on-connection connection bind-dn password)] - (merge (ldap-result r) - (when-let [diagnostic-message (.getDiagnosticMessage r)] - {:diagnostic-message diagnostic-message})))) + (merge (ldap-result r) + (when-let [diagnostic-message (.getDiagnosticMessage r)] + {:diagnostic-message diagnostic-message})))) (defn bind? "Performs a bind operation using the provided connection, bindDN and @@ -612,7 +612,7 @@ "Adds an entry to the connected ldap server. The entry is assumed to be a map. The options map supports control :proxied-auth." ([connection dn entry] - (add connection dn entry nil)) + (add connection dn entry nil)) ([connection dn entry options] (let [entry-obj (Entry. dn)] (set-entry-map! entry-obj entry) @@ -658,7 +658,7 @@ Where :add adds an attribute value, :delete deletes an attribute value and The entries :pre-read and :post-read specify attributes that have be read and returned either before or after the modifications have taken place." ([connection dn modifications] - (modify connection dn modifications nil)) + (modify connection dn modifications nil)) ([connection dn modifications options] (let [modify-obj (get-modify-request dn modifications)] (when options @@ -691,7 +691,7 @@ returned either before or after the modifications have taken place." RDN value from the target entry. The options map supports pre/post-read and proxied-auth controls." ([connection dn new-rdn delete-old-rdn] - (modify-rdn connection dn new-rdn delete-old-rdn nil)) + (modify-rdn connection dn new-rdn delete-old-rdn nil)) ([connection dn new-rdn delete-old-rdn options] (let [request (ModifyDNRequest. dn new-rdn delete-old-rdn)] (when options From 77b96fc43baa8e106f9eba4349eb41474614ef38 Mon Sep 17 00:00:00 2001 From: Brian Williams Date: Mon, 16 Nov 2020 08:26:02 -0500 Subject: [PATCH 4/5] Ignore access logs from test execution --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7114fec..4a4aaa4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,4 @@ pom.xml.asc .idea clj-ldap.iml test-resources/access -test-resources/access.lck +test-resources/access.* From 6975185835767707b342f97c751f48ec38cbc070 Mon Sep 17 00:00:00 2001 From: Brian Williams Date: Mon, 16 Nov 2020 08:33:21 -0500 Subject: [PATCH 5/5] Note the 0.0.17 release --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1367967..083ff0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [0.0.17] +This release adds the following: +- A `bind` function which behaves similarly to `bind?` except that it throws `LDAPException` on failure. Thanks to @yogthos and @sumbach. +- A bump of the ldap-sdk to 5.1.1. + ## [0.0.16] This release adds the following: - A lazy-seq returned by `search-all-results` using SimplePagedResults control. Thanks to @jindrichmynarz.