Merge lp:~matt.hall/endroid/ldap into lp:endroid

Proposed by Matthew Hall
Status: Merged
Approved by: Matthew Hall
Approved revision: 101
Merged at revision: 101
Proposed branch: lp:~matt.hall/endroid/ldap
Merge into: lp:endroid
Diff against target: 77 lines (+58/-5)
1 file modified
src/endroid/plugins/ldapauth.py (+58/-5)
To merge this branch: bzr merge lp:~matt.hall/endroid/ldap
Reviewer Review Type Date Requested Status
Phil Connell Approve
Review via email: mp+281035@code.launchpad.net

Commit message

Enhance the ldapauth plugin to support LDAP directories that do not allow anonymous access.

Description of the change

Enhance the ldapauth plugin to support LDAP directories that do not allow anonymous access.

The Twisted ldaptor module, used by Endroid's ldapauth plugin, unfortunately doesn't already provide a suitable credential checker for this purpose, so we instead define one in the plugin. This is done by sub-classing ldaptor's existing checker, overriding the callback called once connected to the directory so that it skips the attempt to lookup the user's entry and instead proceeds straight to the authentication attempt (i.e. the LDAP bind.)

Skipping the entry look up means the name of the attribute forming the entry's relative distinguished name (e.g. "cn" or "name") must be provided to the checker some other way, so the plugin now supports an "identityrdn" config option.

To post a comment you must log in.
Revision history for this message
Phil Connell (pconnell) wrote :

This is fine to go in to get things working, but strictly speaking it's a massive hack since it relies on internal details (e.g. _connect method and semantics) of ldaptor.

An ideal outcome would be getting a patch into ldaptor upstream to implement this within ldaptor: https://github.com/twisted/ldaptor

review: Approve
Revision history for this message
Matthew Hall (matt.hall) wrote :

> This is fine to go in to get things working, but strictly speaking it's a
> massive hack since it relies on internal details (e.g. _connect method and
> semantics) of ldaptor.
>
> An ideal outcome would be getting a patch into ldaptor upstream to implement
> this within ldaptor: https://github.com/twisted/ldaptor

Thanks, Phil. Yes, agreed it's a hack. Good point about submitting a patch to ldaptor; I'll try to do that over Xmas :-)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/endroid/plugins/ldapauth.py'
2--- src/endroid/plugins/ldapauth.py 2014-04-05 11:42:51 +0000
3+++ src/endroid/plugins/ldapauth.py 2015-12-20 12:23:42 +0000
4@@ -3,7 +3,6 @@
5 #
6
7 from ldaptor import checkers, config
8-from ldaptor.protocols.ldap import distinguishedname
9
10 from endroid.pluginmanager import Plugin
11
12@@ -11,7 +10,61 @@
13 hidden = True
14 name = "ldapauth"
15 def http_cred_checker(self):
16- return checkers.LDAPBindingChecker(
17- config.LDAPConfig(','.join(self.vars.get("basedn")),
18- {','.join(self.vars.get("basedn")): (self.vars.get("ldaphost"),
19- int(self.vars.get("ldapport", 389)))}))
20+ # Check whether we've been configured with the attribute forming the
21+ # relative DN for identity entries within the directory and use the
22+ # appropriate LDAP credential checker object.
23+ rdn = self.vars.get("identityrdn")
24+ if rdn:
25+ return LDAPAuthedBindingChecker(
26+ LDAPAuthedConfig(','.join(self.vars.get("basedn")),
27+ {','.join(self.vars.get("basedn")):
28+ (self.vars.get("ldaphost"),
29+ int(self.vars.get("ldapport", 389)))},
30+ identityRelativeDN=rdn))
31+ else:
32+ return checkers.LDAPBindingChecker(
33+ config.LDAPConfig(','.join(self.vars.get("basedn")),
34+ {','.join(self.vars.get("basedn")):
35+ (self.vars.get("ldaphost"),
36+ int(self.vars.get("ldapport", 389)))}))
37+
38+# Subclass the ldaptor package's LDAPBindingChecker with a variant that
39+# supplies the user's credentials to the bind call; do this by overriding the
40+# _connected callback method.
41+#
42+# On the assumption that this will be used when anonymous access to the LDAP
43+# server is not allowed, the user's entry is not looked up before binding.
44+# Instead the full distinguished name of the user is constructed from this
45+# plugin's config.
46+#
47+# Also subclass LDAPConfig, so that we can attach an extra bit of config to
48+# it.
49+class LDAPAuthedBindingChecker(checkers.LDAPBindingChecker):
50+ def _connected(self, client, filt, credentials):
51+ d = client.bind(self.config.getIdentityDN(credentials.username),
52+ credentials.password)
53+ d.addCallback(self._bound)
54+ return d
55+
56+ def _bound(self, bindresult):
57+ # Doesn't matter what we return here: it's never used.
58+ return bindresult
59+
60+class LDAPAuthedConfig(config.LDAPConfig):
61+ def __init__(self,
62+ baseDN=None,
63+ serviceLocationOverrides=None,
64+ identityBaseDN=None,
65+ identitySearch=None,
66+ identityRelativeDN="cn"):
67+ super(LDAPAuthedConfig, self).__init__(baseDN,
68+ serviceLocationOverrides,
69+ identityBaseDN,
70+ identitySearch)
71+ self.identityRelativeDN = identityRelativeDN
72+
73+ def getIdentityDN(self, name):
74+ return "{}={},{}".format(self.identityRelativeDN,
75+ name,
76+ str(self.getIdentityBaseDN()))
77+

Subscribers

People subscribed via source and target branches