Details
- Applies to
- moin 1.9.4
- Purpose
- Implement LDAP/SASL authentication
- Description
- This patch implements SASL binding for LDAP auth. This has severaladvantages over simple LDAP binding: No bind DN is necessary, and advanced authentication mechanisms are avaiable (e.g. avoiding plain-text transmission of passwords). DIGEST-MD5 tested (that's the only method my LDAP server supports), but other methods should also work.
I am attaching also a small bug fix patch (to be applied before the SASL patch) that fixes the behavior of Moin when LDAPAuth is used before other methods in the auth chain. The latter patch is similar to the patch in MoinMoinBugs/ChainingAuthMethods.
Patch
1 From bc7ca56f41194a33cc82b261238a224b6a634aa2 Mon Sep 17 00:00:00 2001
2 From: Martin Wilck <martin.wilck@ts.fujitsu.com>
3 Date: Fri, 25 May 2012 16:03:07 +0200
4 Subject: [PATCH 1/2] PATCH: [LDAPAuth] allow authentication to continue
5
6 This patch removes use of CancelLogin when bad LDAP credentials
7 were encountered, allowing other auth methods to continue after
8 LDAP failure.
9
10 Furthermore, in the case of LDAP bind failure, the cases with
11 and with out bind_dn are more cleanly distinguished.
12 ---
13 auth/ldap_login.py | 10 ++++++++--
14 1 files changed, 8 insertions(+), 2 deletions(-)
15
16 diff --git a/auth/ldap_login.py b/auth/ldap_login.py
17 index 29dba6e..ba93098 100644
18 --- a/auth/ldap_login.py
19 +++ b/auth/ldap_login.py
20 @@ -27,7 +27,7 @@ except ImportError, err:
21 raise
22
23 from MoinMoin import user
24 -from MoinMoin.auth import BaseAuth, CancelLogin, ContinueLogin
25 +from MoinMoin.auth import BaseAuth, ContinueLogin
26
27
28 class LDAPAuth(BaseAuth):
29 @@ -242,8 +242,14 @@ class LDAPAuth(BaseAuth):
30 logging.debug("creating user object with name %r email %r alias %r" % (username, email, aliasname))
31
32 except ldap.INVALID_CREDENTIALS, err:
33 + if self.bind_once:
34 + if self.report_invalid_credentials:
35 + return ContinueLogin(user_obj,
36 + message=_("Invalid username or password."))
37 + else:
38 + return ContinueLogin(user_obj)
39 logging.debug("invalid credentials (wrong password?) for dn %r (username: %r)" % (dn, username))
40 - return CancelLogin(_("Invalid username or password."))
41 + return ContinueLogin(user_obj, _("Bind to LDAP server %(server)s failed." % {'server': server}))
42
43 if u and self.autocreate:
44 logging.debug("calling create_or_update to autocreate user %r" % u.name)
45 --
46 1.7.7.6
0001-PATCH-LDAPAuth-allow-authentication-to-continue.patch This is the bug fix patch "[PATCH 1/2] PATCH: [LDAPAuth] allow authentication to continue"
1 From 829f13caed40bb7702d3653d76dc56a3c2bded19 Mon Sep 17 00:00:00 2001
2 From: Martin Wilck <martin.wilck@ts.fujitsu.com>
3 Date: Fri, 25 May 2012 16:06:38 +0200
4 Subject: [PATCH 2/2] PATCH: [LDAPAuth] implement SASL bind
5
6 This patch implements SASL binding for LDAP auth. This has several
7 advantages over simple LDAP binding: No bind DN is necessary, and
8 advanced authentication mechanisms are avaiable (e.g. avoiding
9 plain-text transmission of passwords).
10
11 DIGEST-MD5 tested (that's the only method my LDAP server supports),
12 but other methods should also work.
13 ---
14 auth/ldap_login.py | 40 ++++++++++++++++++++++++++++++++++++++--
15 1 files changed, 38 insertions(+), 2 deletions(-)
16
17 diff --git a/auth/ldap_login.py b/auth/ldap_login.py
18 index ba93098..5482e8a 100644
19 --- a/auth/ldap_login.py
20 +++ b/auth/ldap_login.py
21 @@ -26,6 +26,15 @@ except ImportError, err:
22 logging.error("You need to have python-ldap installed (%s)." % str(err))
23 raise
24
25 +try:
26 + import ldap.sasl as ldap_sasl
27 +except ImportError, err:
28 + logging.error("SASL module not found, SASL bind will not be possible (%s)." % str(err))
29 + ldap.sasl = None
30 +else:
31 + ldap.sasl = ldap_sasl
32 + del ldap_sasl
33 +
34 from MoinMoin import user
35 from MoinMoin.auth import BaseAuth, ContinueLogin
36
37 @@ -85,6 +94,7 @@ class LDAPAuth(BaseAuth):
38 autocreate=False, # set to True if you want to autocreate user profiles
39 name='ldap', # use e.g. 'ldap_pdc' and 'ldap_bdc' (or 'ldap1' and 'ldap2') if you auth against 2 ldap servers
40 report_invalid_credentials=True, # whether to emit "invalid username or password" msg at login time or not
41 + sasl_mech=None, # The SASL mechnism to use - this activates SASL
42 ):
43 self.server_uri = server_uri
44 self.bind_dn = bind_dn
45 @@ -117,6 +127,24 @@ class LDAPAuth(BaseAuth):
46
47 self.report_invalid_credentials = report_invalid_credentials
48
49 + if ldap.sasl is None:
50 + self.sasl_mech = None
51 + else:
52 + self.sasl_mech = sasl_mech.upper()
53 + self.bind_once = True # There's only 1 bind with SASL
54 +
55 + def sasl_bind(self, connection, authname, passwd):
56 + if self.sasl_mech in ("GSSAPI", "EXTERNAL"):
57 + authargs = {}
58 + else:
59 + authargs = {
60 + ldap.sasl.CB_AUTHNAME: authname.encode(self.coding),
61 + ldap.sasl.CB_PASS: passwd.encode(self.coding)
62 + }
63 + logging.debug("Attempting SASL/%s bind for %s" % (self.sasl_mech, authname))
64 + authtok = ldap.sasl.sasl(authargs, self.sasl_mech)
65 + connection.sasl_interactive_bind_s("", authtok)
66 +
67 def login(self, request, user_obj, **kw):
68 username = kw.get('username')
69 password = kw.get('password')
70 @@ -168,8 +196,11 @@ class LDAPAuth(BaseAuth):
71 # you can use %(username)s and %(password)s here to get the stuff entered in the form:
72 binddn = self.bind_dn % locals()
73 bindpw = self.bind_pw % locals()
74 - l.simple_bind_s(binddn.encode(coding), bindpw.encode(coding))
75 - logging.debug("Bound with binddn %r" % binddn)
76 + if self.sasl_mech is None:
77 + l.simple_bind_s(binddn.encode(coding), bindpw.encode(coding))
78 + logging.debug("Bound with binddn %r" % binddn)
79 + else:
80 + self.sasl_bind(l, binddn, bindpw)
81
82 # you can use %(username)s here to get the stuff entered in the form:
83 filterstr = self.search_filter % locals()
84 @@ -265,6 +296,11 @@ class LDAPAuth(BaseAuth):
85 "Trying to authenticate with next auth list entry." % (server, str(err)))
86 return ContinueLogin(user_obj, _("LDAP server %(server)s failed.") % {'server': server})
87
88 + except (ldap.STRONG_AUTH_NOT_SUPPORTED, ldap.STRONG_AUTH_REQUIRED, ldap.AUTH_UNKNOWN), err:
89 + logging.error("Authentication to LDAP server %s failed (%s)."
90 + "Trying to authenticate with next auth list entry." % (server, str(err)))
91 + return ContinueLogin(user_obj, _("LDAP server %(server)s failed.") % {'server': server})
92 +
93 except:
94 logging.exception("caught an exception, traceback follows...")
95 return ContinueLogin(user_obj)
96 --
97 1.7.7.6
0002-PATCH-LDAPAuth-implement-SASL-bind.patch This patch implements the SASL authentication.
Discussion
Plan
- Priority:
- Assigned to:
- Status: