[Buildbot-devel] [PATCH 12/12 v3] Implement an LDAP based authentication for the WebStatus.
Benoit Sigoure
tsuna at lrde.epita.fr
Wed Nov 28 13:07:52 UTC 2007
* NEWS: Mention the change.
* buildbot/status/web/authentication.py (LDAPAuth): New class.
* docs/buildbot.texinfo (WebStatus Configuration Parameters):
Document the new feature.
Signed-off-by: Benoit Sigoure <tsuna at lrde.epita.fr>
---
This is a resend with a one-line fix in LDAPAuth.authenticate that adds a
missing `import ldap' statement.
NEWS | 3 +-
buildbot/status/web/authentication.py | 101 +++++++++++++++++++++++++++++++++
docs/buildbot.texinfo | 9 +++-
3 files changed, 111 insertions(+), 2 deletions(-)
diff --git a/NEWS b/NEWS
index 9ef04a4..d687e2a 100644
--- a/NEWS
+++ b/NEWS
@@ -11,7 +11,8 @@ User visible changes in Buildbot. -*- outline -*-
The WebStatus constructor can take an instance of IAuth (from
status.web.authentication). The class BasicAuth accepts a `userpass' keyword
argument in pretty much the same way as Try_Userpass does. The class
-HTPasswdAuth authenticate users with a .htpasswd-style file.
+HTPasswdAuth authenticate users with a .htpasswd-style file. The class
+LDAPAuth authenticate users with an LDAP directory (requires python-ldap).
Only users with a valid login/password can then force/stop builds from the
WebStatus.
diff --git a/buildbot/status/web/authentication.py b/buildbot/status/web/authentication.py
index a7e8527..46a122b 100644
--- a/buildbot/status/web/authentication.py
+++ b/buildbot/status/web/authentication.py
@@ -88,3 +88,104 @@ class HTPasswdAuth(AuthBase):
else:
self.err = "Invalid password"
return res
+
+class LDAPAuth(AuthBase):
+ implements(IAuth)
+ """Implement a synchronous authentication with an LDAP directory."""
+
+ basedn = ""
+ """Base DN (Distinguished Name): the root of the LDAP directory tree
+
+ e.g.: ou=people,dc=subdomain,dc=company,dc=com"""
+
+ binddn = ""
+ """The bind DN is the user on the external LDAP server permitted to
+ search the LDAP directory. You can leave this empty."""
+
+ passwd = ""
+ """Password required to query the LDAP server. Leave this empty if
+ you can query the server without password."""
+
+ host = ""
+ """Hostname of the LDAP server"""
+
+ search = ""
+ """Template string to use to search the user trying to login in the
+ LDAP directory"""
+
+ def __init__(self, host, basedn, binddn="", passwd="",
+ search="(uid=%s)"):
+ """Authenticate users against the LDAP server on C{host}.
+
+ The arguments are documented above."""
+ self.host = host
+ self.basedn = basedn
+ self.binddn = binddn
+ self.passwd = passwd
+ self.search = search
+
+ self.search_conn = None
+ self.connect()
+
+ def connect(self):
+ """Setup the connections with the LDAP server."""
+ import ldap
+ # Close existing connections
+ if self.search_conn:
+ self.search_conn.unbind()
+ # Connection used to locate the users in the LDAP DB.
+ self.search_conn = ldap.open(self.host)
+ self.search_conn.bind_s(self.binddn, self.passwd,
+ ldap.AUTH_SIMPLE)
+
+ def authenticate(self, login, password):
+ """Authenticate the C{login} / C{password} with the LDAP server."""
+ import ldap
+ # Python-LDAP raises all sorts of exceptions to express various
+ # failures, let's catch them all here and assume that if
+ # anything goes wrong, the authentication failed.
+ try:
+ res = self._authenticate(login, password)
+ if res:
+ self.err = ""
+ return res
+ except ldap.LDAPError, e:
+ self.err = "LDAP error: " + str(e)
+ return False
+ except:
+ self.err = "unknown error"
+ return False
+
+ def _authenticate(self, login, password):
+ import ldap
+ # Search the login in the LDAP DB
+ try:
+ result = self.search_conn.search_s(self.basedn,
+ ldap.SCOPE_SUBTREE,
+ self.search % login,
+ ['objectclass'], 1)
+ except ldap.SERVER_DOWN:
+ self.err = "LDAP server seems down"
+ # Try to reconnect...
+ self.connect()
+ # FIXME: Check that this can't lead to an infinite recursion
+ return self.authenticate(login, password)
+
+ # Make sure we found a single user in the LDAP DB
+ if not result or len(result) != 1:
+ self.err = "user not found in the LDAP DB"
+ return False
+
+ # Connection used to authenticate users with the LDAP DB.
+ auth_conn = ldap.open(self.host)
+ # DN associated to this user
+ ldap_dn = result[0][0]
+ #log.msg('using ldap_dn = ' + ldap_dn)
+ # Authenticate the user
+ try:
+ auth_conn.bind_s(ldap_dn, password, ldap.AUTH_SIMPLE)
+ except ldap.INVALID_CREDENTIALS:
+ self.err = "invalid credentials"
+ return False
+ auth_conn.unbind()
+ return True
diff --git a/docs/buildbot.texinfo b/docs/buildbot.texinfo
index 098ab63..301941b 100644
--- a/docs/buildbot.texinfo
+++ b/docs/buildbot.texinfo
@@ -6071,7 +6071,9 @@ current builds, in that case you can pass an instance of
The class @code{BasicAuth} implements a basic authentication mechanism
using a list of login/password pairs provided from the configuration file.
The class @code{HTPasswdAuth} implements an authentication against an
- at file{.htpasswd}-style file.
+ at file{.htpasswd}-style file. The class @code{LDAPAuth} authenticates
+the users with an LDAP database. It requires
+ at uref{http://python-ldap.sourceforge.net/, python-ldap}.
@example
from buildbot.status.html import WebStatus
@@ -6082,6 +6084,11 @@ c['status'].append(WebStatus(http_port=8080, auth=BasicAuth(users)))
from buildbot.status.web.authentication import HTPasswdAuth
file = '/path/to/file'
c['status'].append(WebStatus(http_port=8080, auth=HTPasswdAuth(file)))
+
+from buildbot.status.web.authentication import LDAPAuth
+c['status'].append(WebStatus(http_port=8080,
+ auth=LDAPAuth('ldap.server.company.com',
+ 'ou=people,dc=company,dc=com'))
@end example
--
1.5.3.5.756.gc887a
More information about the devel
mailing list