LDAP: Allow specification of multiple servers as fallbacks

Resolves #933

Change-Id: I30f16778d0031620a07f1e35601d22251e5c6883
diff --git a/src/test/java/de/ids_mannheim/korap/authentication/LdapAuth3Test.java b/src/test/java/de/ids_mannheim/korap/authentication/LdapAuth3Test.java
index d6b81f5..f45bce9 100644
--- a/src/test/java/de/ids_mannheim/korap/authentication/LdapAuth3Test.java
+++ b/src/test/java/de/ids_mannheim/korap/authentication/LdapAuth3Test.java
@@ -30,6 +30,10 @@
 
     public static final String TEST_LDAPS_TS_CONF = "src/test/resources/test-ldaps-with-truststore.conf";
 
+    public static final String TEST_LDAP_FALLBACK_CONF = "src/test/resources/test-ldap-fallback.conf";
+    public static final String TEST_LDAP_FALLBACK_FAIL_CONF = "src/test/resources/test-ldap-fallback-fail.conf";
+    public static final String TEST_LDAP_FALLBACK_PORT_CONF = "src/test/resources/test-ldap-fallback-port.conf";
+
     public static final String TEST_LDAP_USERS_LDIF = "src/test/resources/test-ldap-users.ldif";
 
     private static final String keyStorePath = "src/test/resources/keystore.p12";
@@ -245,4 +249,30 @@
         assertEquals(3269, ldapConfig.port);
         assertEquals("localhost", ldapConfig.host);
     }
+
+    @Test
+    public void loginWithFallbackWorks () throws LDAPException {
+        // `test-ldap-fallback.conf` defines `host = invalid.local, localhost`
+        // Should connect to localhost successfully after failing on invalid.local
+        assertEquals(LDAP_AUTH_ROK,
+                LdapAuth3.login("testuser", "password", TEST_LDAP_FALLBACK_CONF));
+    }
+
+    @Test
+    public void loginWithFallbackFails () throws LDAPException {
+        // `test-ldap-fallback-fail.conf` defines `host = invalid1.local, invalid2.local`
+        // Should return LDAP_AUTH_RCONNECT or LDAP_AUTH_RTIMEOUT depending on network
+        int rc = LdapAuth3.login("testuser", "password", TEST_LDAP_FALLBACK_FAIL_CONF);
+        assertTrue(rc == LDAP_AUTH_RCONNECT || rc == LDAP_AUTH_RTIMEOUT,
+                "Expected connection failure, but got code=" + rc);
+    }
+
+    @Test
+    public void loginWithFallbackAndPortWorks () throws LDAPException {
+        // `test-ldap-fallback-port.conf` defines `host = invalid.local, localhost:3268`
+        // and a default port of 1111 which is wrong
+        // Should connect to localhost on port 3268 specifically
+        assertEquals(LDAP_AUTH_ROK,
+                LdapAuth3.login("testuser", "password", TEST_LDAP_FALLBACK_PORT_CONF));
+    }
 }
diff --git a/src/test/resources/test-ldap-fallback-fail.conf b/src/test/resources/test-ldap-fallback-fail.conf
new file mode 100644
index 0000000..87eb005
--- /dev/null
+++ b/src/test/resources/test-ldap-fallback-fail.conf
@@ -0,0 +1,6 @@
+host = invalid1.local, invalid2.local
+port = 3268
+searchBase = dc=example,dc=com
+sLoginDN = cn=admin,dc=example,dc=com
+pwd = adminpassword
+searchFilter=(&(|(uid=${login})(mail=${login})(extraProfile=${login}))(|(userPassword=${password})(extraPassword=${password})))
diff --git a/src/test/resources/test-ldap-fallback-port.conf b/src/test/resources/test-ldap-fallback-port.conf
new file mode 100644
index 0000000..5f88fc7
--- /dev/null
+++ b/src/test/resources/test-ldap-fallback-port.conf
@@ -0,0 +1,6 @@
+host = invalid.local, localhost:3268
+port = 1111
+searchBase = dc=example,dc=com
+sLoginDN = cn=admin,dc=example,dc=com
+pwd = adminpassword
+searchFilter=(&(|(uid=${login})(mail=${login})(extraProfile=${login}))(|(userPassword=${password})(extraPassword=${password})))
diff --git a/src/test/resources/test-ldap-fallback.conf b/src/test/resources/test-ldap-fallback.conf
new file mode 100644
index 0000000..51ab913
--- /dev/null
+++ b/src/test/resources/test-ldap-fallback.conf
@@ -0,0 +1,6 @@
+host = invalid.local, localhost
+port = 3268
+searchBase = dc=example,dc=com
+sLoginDN = cn=admin,dc=example,dc=com
+pwd = adminpassword
+searchFilter=(&(|(uid=${login})(mail=${login})(extraProfile=${login}))(|(userPassword=${password})(extraPassword=${password})))