diff --git a/src/metabase/integrations/ldap.clj b/src/metabase/integrations/ldap.clj index 392aea2facf37675917d4bed5015ecb1e45144f3..19544324ba51b1513ceb9f7b23b87e0301594d2d 100644 --- a/src/metabase/integrations/ldap.clj +++ b/src/metabase/integrations/ldap.clj @@ -95,7 +95,12 @@ (ldap-user-base))))) (defn- details->ldap-options [{:keys [host port bind-dn password security]}] - {:host (str host ":" port) + ;; Connecting via IPv6 requires us to use this form for :host, otherwise + ;; clj-ldap will find the first : and treat it as an IPv4 and port number + {:host {:address host + :port (if (string? port) + (Integer/parseInt port) + port)} :bind-dn bind-dn :password password :ssl? (= security "ssl") diff --git a/test/metabase/integrations/ldap_test.clj b/test/metabase/integrations/ldap_test.clj index 0ad3bc1a860f27955a91c9bb9d832adfc58c88f5..67cac8b8db7d0605fbdb60844e13971c0ef39183 100644 --- a/test/metabase/integrations/ldap_test.clj +++ b/test/metabase/integrations/ldap_test.clj @@ -1,5 +1,5 @@ (ns metabase.integrations.ldap-test - (:require [expectations :refer [expect]] + (:require [clojure.test :refer :all] [metabase.integrations.ldap :as ldap] [metabase.test.integrations.ldap :as ldap.test] [metabase.test.util :as tu])) @@ -16,109 +16,106 @@ ;; See test_resources/ldap.ldif for fixtures ;; The connection test should pass with valid settings -(expect - {:status :SUCCESS} - (ldap.test/with-ldap-server - (ldap/test-ldap-connection (get-ldap-details)))) - -;; The connection test should allow anonymous binds -(expect - {:status :SUCCESS} - (ldap.test/with-ldap-server - (ldap/test-ldap-connection (dissoc (get-ldap-details) :bind-dn)))) - -;; The connection test should fail with an invalid user search base -(expect - :ERROR - (ldap.test/with-ldap-server - (:status (ldap/test-ldap-connection (assoc (get-ldap-details) :user-base "dc=example,dc=com"))))) - -;; The connection test should fail with an invalid group search base -(expect - :ERROR - (ldap.test/with-ldap-server - (:status (ldap/test-ldap-connection (assoc (get-ldap-details) :group-base "dc=example,dc=com"))))) - -;; The connection test should fail with an invalid bind DN -(expect - :ERROR - (ldap.test/with-ldap-server - (:status (ldap/test-ldap-connection (assoc (get-ldap-details) :bind-dn "cn=Not Directory Manager"))))) - -;; The connection test should fail with an invalid bind password -(expect - :ERROR - (ldap.test/with-ldap-server - (:status (ldap/test-ldap-connection (assoc (get-ldap-details) :password "wrong"))))) - -;; Make sure the basic connection stuff works, this will throw otherwise -(expect - nil - (ldap.test/with-ldap-server - (.close (#'ldap/get-connection)))) - -;; Login with everything right should succeed -(expect - (ldap.test/with-ldap-server - (ldap/verify-password "cn=Directory Manager" "password"))) - -;; Login with wrong password should fail -(expect - false - (ldap.test/with-ldap-server - (ldap/verify-password "cn=Directory Manager" "wrongpassword"))) - -;; Login with invalid DN should fail -(expect - false - (ldap.test/with-ldap-server - (ldap/verify-password "cn=Nobody,ou=nowhere,dc=metabase,dc=com" "password"))) - -;; Login for regular users should also work -(expect - (ldap.test/with-ldap-server - (ldap/verify-password "cn=Sally Brown,ou=People,dc=metabase,dc=com" "1234"))) - -;; Login for regular users should also fail for the wrong password -(expect - false - (ldap.test/with-ldap-server - (ldap/verify-password "cn=Sally Brown,ou=People,dc=metabase,dc=com" "password"))) - -;; Find by username should work (given the default LDAP filter and test fixtures) -(expect - {:dn "cn=John Smith,ou=People,dc=metabase,dc=com" - :first-name "John" - :last-name "Smith" - :email "John.Smith@metabase.com" - :groups ["cn=Accounting,ou=Groups,dc=metabase,dc=com"]} - (ldap.test/with-ldap-server - (ldap/find-user "jsmith1"))) - -;; Find by email should also work (also given our default settings and fixtures) -(expect - {:dn "cn=John Smith,ou=People,dc=metabase,dc=com" - :first-name "John" - :last-name "Smith" - :email "John.Smith@metabase.com" - :groups ["cn=Accounting,ou=Groups,dc=metabase,dc=com"]} - (ldap.test/with-ldap-server - (ldap/find-user "John.Smith@metabase.com"))) - -;; Find by email should also work (also given our default settings and fixtures) -(expect - {:dn "cn=Fred Taylor,ou=People,dc=metabase,dc=com" - :first-name "Fred" - :last-name "Taylor" - :email "fred.taylor@metabase.com" - :groups []} - (ldap.test/with-ldap-server - (ldap/find-user "fred.taylor@metabase.com"))) - -;; LDAP group matching should identify Metabase groups using DN equality rules -(expect - #{1 2 3} - (tu/with-temporary-setting-values [ldap-group-mappings {"cn=accounting,ou=groups,dc=metabase,dc=com" [1 2] - "cn=shipping,ou=groups,dc=metabase,dc=com" [2 3]}] - (#'ldap/ldap-groups->mb-group-ids ["CN=Accounting,OU=Groups,DC=metabase,DC=com" - "CN=Shipping,OU=Groups,DC=metabase,DC=com"]))) +(deftest connection-test + (testing "anonymous binds" + (testing "successfully connect to IPv4 host" + (is (= {:status :SUCCESS} + (ldap.test/with-ldap-server + (ldap/test-ldap-connection (get-ldap-details))))))) + + (testing "invalid user search base" + (is (= :ERROR + (ldap.test/with-ldap-server + (:status (ldap/test-ldap-connection (assoc (get-ldap-details) + :user-base "dc=example,dc=com"))))))) + + (testing "invalid group search base" + (is (= :ERROR + (ldap.test/with-ldap-server + (:status (ldap/test-ldap-connection (assoc (get-ldap-details) :group-base "dc=example,dc=com"))))))) + + (testing "invalid bind DN" + (is (= :ERROR + (ldap.test/with-ldap-server + (:status (ldap/test-ldap-connection (assoc (get-ldap-details) :bind-dn "cn=Not Directory Manager"))))))) + + (testing "invalid bind password" + (is (= :ERROR + (ldap.test/with-ldap-server + (:status (ldap/test-ldap-connection (assoc (get-ldap-details) :password "wrong"))))))) + + (testing "basic get-connection works, will throw otherwise" + (is (= nil + (ldap.test/with-ldap-server + (.close (#'ldap/get-connection)))))) + + (testing "login should succeed" + (is (= true + (ldap.test/with-ldap-server + (ldap/verify-password "cn=Directory Manager" "password"))))) + + (testing "wrong password" + (is (= false + (ldap.test/with-ldap-server + (ldap/verify-password "cn=Directory Manager" "wrongpassword"))))) + + (testing "invalid DN fails" + (is (= false + (ldap.test/with-ldap-server + (ldap/verify-password "cn=Nobody,ou=nowhere,dc=metabase,dc=com" "password"))))) + + (testing "regular user login" + (is (= true + (ldap.test/with-ldap-server + (ldap/verify-password "cn=Sally Brown,ou=People,dc=metabase,dc=com" "1234"))))) + + (testing "fail regular user login with bad password" + (is (= false + (ldap.test/with-ldap-server + (ldap/verify-password "cn=Sally Brown,ou=People,dc=metabase,dc=com" "password"))))) + + (testing "find by username" + (is (= {:dn "cn=John Smith,ou=People,dc=metabase,dc=com" + :first-name "John" + :last-name "Smith" + :email "John.Smith@metabase.com" + :groups ["cn=Accounting,ou=Groups,dc=metabase,dc=com"]} + (ldap.test/with-ldap-server + (ldap/find-user "jsmith1"))))) + + (testing "find by email" + (is (= {:dn "cn=John Smith,ou=People,dc=metabase,dc=com" + :first-name "John" + :last-name "Smith" + :email "John.Smith@metabase.com" + :groups ["cn=Accounting,ou=Groups,dc=metabase,dc=com"]} + (ldap.test/with-ldap-server + (ldap/find-user "John.Smith@metabase.com"))))) + + (testing "find by email, no groups" + (is (= {:dn "cn=Fred Taylor,ou=People,dc=metabase,dc=com" + :first-name "Fred" + :last-name "Taylor" + :email "fred.taylor@metabase.com" + :groups []} + (ldap.test/with-ldap-server + (ldap/find-user "fred.taylor@metabase.com"))))) + + (testing "LDAP group matching should identify Metabase groups using DN equality rules" + (is (= #{1 2 3} + (tu/with-temporary-setting-values + [ldap-group-mappings {"cn=accounting,ou=groups,dc=metabase,dc=com" [1 2] + "cn=shipping,ou=groups,dc=metabase,dc=com" [2 3]}] + (#'ldap/ldap-groups->mb-group-ids ["CN=Accounting,OU=Groups,DC=metabase,DC=com" + "CN=Shipping,OU=Groups,DC=metabase,DC=com"])))))) + +;; For hosts that do not support IPv6, the connection code will return an error +;; This isn't a failure of the code, it's a failure of the host. +(deftest ipv6-test + (testing "successfully connect to IPv6 host" + (let [actual (ldap.test/with-ldap-server + (ldap/test-ldap-connection (assoc (get-ldap-details) + :host "[::1]")))] + (if (= (:status actual) :ERROR) + (is (re-matches #"An error occurred while attempting to connect to server \[::1].*" (:message actual))) + (is (= {:status :SUCCESS} actual))))))