[Buildbot-devel] [PATCH v2 11/12] Allow authentication to work with	.htpasswd-style files. 	* NEWS: Mention the change.
	* buildbot/status/web/authentication.py (HTPasswdAuth): New class.
	* docs/buildbot.texinfo (WebStatus Configuration Parameters):
	Document the new feature.

Signed-off-by: Benoit Sigoure 
---
 NEWS                                  |    3 +-
 buildbot/status/web/authentication.py |   39 +++++++++++++++++++++++++++++++++
 docs/buildbot.texinfo                 |    6 +++++
 3 files changed, 47 insertions(+), 1 deletions(-)

diff --git a/NEWS b/NEWS
index 4f3b499..9ef04a4 100644
--- a/NEWS
+++ b/NEWS
@@ -10,7 +10,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.
+argument in pretty much the same way as Try_Userpass does.  The class
+HTPasswdAuth authenticate users with a .htpasswd-style file.
 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 f6e1b75..a7e8527 100644
--- a/buildbot/status/web/authentication.py
+++ b/buildbot/status/web/authentication.py
@@ -1,3 +1,4 @@
+import os
 from zope.interface import Interface, implements
 
 class IAuth(Interface):
@@ -49,3 +50,41 @@ class BasicAuth(AuthBase):
                 return True
         self.err = "Invalid login or password"
         return False
+
+class HTPasswdAuth(AuthBase):
+    implements(IAuth)
+    """Implement an authentication against an .htpasswd-style file."""
+
+    file = ""
+    """Path to the .htpasswd-style file to use."""
+
+    def __init__(self, file):
+        """C{file} is a path to an .htpasswd-style file."""
+        assert os.path.exists(file)
+        self.file = file
+
+    def authenticate(self, login, password):
+        """Authenticate C{login} and C{password} against a .htpasswd-style
+        file"""
+        if not os.path.exists(self.file):
+            self.err = "No such file: " + self.file
+            return False
+        # Fetch the lines from the .htpasswd file and split them in a
+        # [login, password] array.
+        lines = [l.rstrip().split(':', 1)
+                 for l in file(self.file).readlines()]
+        # Keep only the line for this login
+        lines = [l for l in lines if l[0] == login]
+        if not lines:
+            self.err = "Invalid login"
+            return False
+        # This is the DES-hash of the password.  The first two characters are
+        # the salt used to introduce disorder in the DES algorithm.
+        hash = lines[0][1]
+        from crypt import crypt
+        res = hash == crypt(password, hash[0:2])
+        if res:
+            self.err = ""
+        else:
+            self.err = "Invalid password"
+        return res
diff --git a/docs/buildbot.texinfo b/docs/buildbot.texinfo
index 8d8e0ef..098ab63 100644
--- a/docs/buildbot.texinfo
+++ b/docs/buildbot.texinfo
@@ -6070,12 +6070,18 @@ current builds, in that case you can pass an instance of
 @code{status.web.authentication.IAuth} as a @code{auth} keyword argument.
 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
+@file{.htpasswd}-style file.
 
 @example
 from buildbot.status.html import WebStatus
 from buildbot.status.web.authentication import BasicAuth
 users = [('login', 'password'), ('bob', 'secret-pass')]
 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)))
 @end example
 
 
-- 
1.5.3.5.756.gc887a


