changeset 3654:879d8bdf01a1

ldap auth: cancel login only for invalid credentials, otherwise give subsequent auth list entries a chance, failover works, improved logging
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sun, 01 Jun 2008 17:44:42 +0200
parents 0b04ad4db631
children ba99c8023063
files MoinMoin/auth/_tests/test_ldap_login.py MoinMoin/auth/ldap_login.py docs/CHANGES
diffstat 3 files changed, 45 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/auth/_tests/test_ldap_login.py	Sun Jun 01 14:49:46 2008 +0200
+++ b/MoinMoin/auth/_tests/test_ldap_login.py	Sun Jun 01 17:44:42 2008 +0200
@@ -79,7 +79,6 @@
 
     def setup_class(self):
         """ Create LDAP servers environment, start slapds """
-        py.test.skip("Failover not implemented yet")
         self.ldap_envs = []
         for instance in range(2):
             ldap_env = LdapEnvironment(self.basedn, self.rootdn, self.rootpw, instance=instance)
@@ -90,7 +89,6 @@
 
     def teardown_class(self):
         """ Stop slapd, remove LDAP server environment """
-        py.test.skip("Failover not implemented yet")
         for ldap_env in self.ldap_envs:
             try:
                 ldap_env.stop_slapd()
@@ -100,7 +98,6 @@
 
     def testLDAP(self):
         """ Just try accessing the LDAP servers and see if usera and userb are in LDAP. """
-        py.test.skip("Failover not implemented yet")
         for ldap_env in self.ldap_envs:
             server_uri = ldap_env.slapd.url
             base_dn = ldap_env.basedn
@@ -114,13 +111,13 @@
 
     def testMoinLDAPLogin(self):
         """ Just try accessing the LDAP server and see if usera and userb are in LDAP. """
-        py.test.skip("Failover not implemented yet")
         from MoinMoin.auth.ldap_login import LDAPAuth
         authlist = []
         for ldap_env in self.ldap_envs:
             server_uri = ldap_env.slapd.url
             base_dn = ldap_env.basedn
-            ldap_auth = LDAPAuth(server_uri=server_uri, base_dn=base_dn)
+            ldap_auth = LDAPAuth(server_uri=server_uri, base_dn=base_dn,
+                                 timeout=1) # short timeout, faster testing
             authlist.append(ldap_auth)
 
         self.config = self.TestConfig(auth=authlist, user_autocreate=True)
--- a/MoinMoin/auth/ldap_login.py	Sun Jun 01 14:49:46 2008 +0200
+++ b/MoinMoin/auth/ldap_login.py	Sun Jun 01 17:44:42 2008 +0200
@@ -17,11 +17,15 @@
                 2006 Nick Phillips
     @license: GNU GPL, see COPYING for details.
 """
-import ldap
-
 from MoinMoin import log
 logging = log.getLogger(__name__)
 
+try:
+    import ldap
+except ImportError, err:
+    logging.error("You need to have python-ldap installed (%s)." % str(err))
+    raise
+
 from MoinMoin import user
 from MoinMoin.auth import BaseAuth, CancelLogin, ContinueLogin
 
@@ -180,10 +184,10 @@
                 result_length = len(lusers)
                 if result_length != 1:
                     if result_length > 1:
-                        logging.debug("Search found more than one (%d) matches for %r." % (result_length, filterstr))
+                        logging.warning("Search found more than one (%d) matches for %r." % (result_length, filterstr))
                     if result_length == 0:
                         logging.debug("Search found no matches for %r." % (filterstr, ))
-                    return CancelLogin(_("Invalid username or password."))
+                    return ContinueLogin(user_obj, _("Invalid username or password."))
 
                 dn, ldap_dict = lusers[0]
                 if not self.bind_once:
@@ -231,7 +235,16 @@
                 u.create_or_update(True)
             return ContinueLogin(u)
 
+        except ldap.SERVER_DOWN, err:
+            # looks like this LDAP server isn't working, so we just try the next
+            # authenticator object in cfg.auth list (there could be some second
+            # ldap authenticator that queries a backup server or any other auth
+            # method).
+            logging.error("LDAP server %s failed (%s). "
+                          "Trying to authenticate with next auth list entry." % (server, str(err)))
+            return ContinueLogin(user_obj, _("LDAP server %(server)s failed." % {'server': server}))
+
         except:
             logging.exception("caught an exception, traceback follows...")
-            return CancelLogin(None)
+            return ContinueLogin(user_obj)
 
--- a/docs/CHANGES	Sun Jun 01 14:49:46 2008 +0200
+++ b/docs/CHANGES	Sun Jun 01 17:44:42 2008 +0200
@@ -47,6 +47,31 @@
     * Event notifications: send notifies in the language of the message
       recipient (not of the current wiki user), other fixes.
 
+  Other changes:
+    * HINT: ldap_login behaves a bit different now:
+      In previous moin versions, ldap_login tended to either successfully
+      authenticate a user or to completely cancel the whole login process in
+      any other case (including ldap server down or exceptions happening).
+      This made subsequent auth list entries rather pointless.
+      Now it behaves like this:
+        * user not found in LDAP -> give subsequent auth list entries a
+          chance to authenticate the user (same happens if it finds multiple
+          LDAP entries when searching - it logs an additional warning then).
+        * user found, but wrong password -> cancel login
+        * ldap server not reachable or other exceptions -> give subsequent
+          auth list entries a chance
+      So please make sure that you really trust every auth list entry you have
+      configured when upgrading or it might maybe change behaviour in a
+      unexpected or unwanted way.
+    * ldap_login now supports failover: if it can't contact your LDAP server
+      (e.g. because it is down or unreachable), it will just continue and
+      try to authenticate with other authenticators (if there are any in
+      cfg.auth list). So if you have some mirroring LDAP backup server, just
+      put another authenticator querying it there:
+          ldap_auth1 = LDAPAuth(server_uri='ldap://mainserver', ...)
+          ldap_auth2 = LDAPAuth(server_uri='ldap://backupserver', ...)
+          auth = [ldap_auth1, ldap_auth2, ]
+
 
 Version 1.7.0rc1: