diff -Nru buildbot-0.7.2/buildbot/ldapauth.py buildbot-0.7.2-auth/buildbot/ldapauth.py
--- buildbot-0.7.2/buildbot/ldapauth.py	1969-12-31 16:00:00.000000000 -0800
+++ buildbot-0.7.2-auth/buildbot/ldapauth.py	2006-04-20 16:43:41.000000000 -0700
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+from twisted.internet import reactor, defer
+from ldaptor.protocols.ldap import ldapclient, ldapsyntax, ldapconnector, \
+                                   distinguishedname
+from ldaptor.protocols import pureldap
+
+class LDAPAuthenticationSource(object):
+    def __init__(self, base_dn, hostname=None, attr='uid', bind_dn='',
+                 bind_pw='', filter=pureldap.LDAPFilter_present('cn')):
+        self.hostname = hostname
+        self.base_dn = base_dn
+        self.attr = attr
+        self.bind_dn = bind_dn
+        self.bind_pw = bind_pw
+        self.filter = filter
+
+    def authenticate(self, username, password):
+        """Returns a deferred that will succeed or fail with the auth."""
+        c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient)
+        return c.connect(self.base_dn
+        ).addCallback(self._connected, username, password)
+
+    def _connected(self, cli, username, password):
+        # Bind a first time before issuing our query.
+        return cli.bind(self.bind_dn, self.bind_pw
+        ).addCallback(self._query_bound, username, password, cli)
+
+    def _query_bound(self, dummy, username, password, cli):
+        # Then issue a search for the uid we want.
+        entries = []
+        baseEntry = ldapsyntax.LDAPEntry(cli, self.base_dn)
+        return baseEntry.search(
+            # This is stupid verbose, but at least it escapes properly.
+            filterObject=pureldap.LDAPFilter_and([
+                pureldap.LDAPFilter_equalityMatch(
+                    attributeDesc=pureldap.LDAPAttributeDescription(self.attr),
+                    assertionValue=pureldap.LDAPAssertionValue(username)
+                ),
+                self.filter,
+            ]),
+            callback=entries.append
+        ).addCallback(self._search_end, password, entries)
+
+    def _search_end(self, dummy, password, entries):
+        n_entries = len(entries)
+        if n_entries == 0:
+            raise Exception('No search results!')
+        elif n_entries > 1:
+            raise Exception('%d search results for unique entry!' % n_entries)
+        else:
+            # The password matches if we can bind as this DN with it.
+            return entries[0].bind(password)
diff -Nru buildbot-0.7.2/buildbot/status/html.py buildbot-0.7.2-auth/buildbot/status/html.py
--- buildbot-0.7.2/buildbot/status/html.py	2006-04-04 17:19:10.000000000 -0700
+++ buildbot-0.7.2-auth/buildbot/status/html.py	2006-04-20 16:44:50.000000000 -0700
@@ -58,6 +58,27 @@
     label = html.escape(label)
     return ROW_TEMPLATE % {"label": label, "field": field}
 
+def authenticate(build_authenticator, request, nextfunc):
+    if build_authenticator is None:
+        # We assume they're authorized.
+        return nextfunc(request)
+    else:
+        # We actually check.
+        name = request.args.get("username", [None])[0]
+        password = request.args.get("password", [None])[0]
+
+        def _success(entry):
+            log.msg('Successful authentication as %r' % name)
+            return nextfunc(request)
+
+        def _failure(f):
+            log.msg('Failed authentication as %r' % name, f)
+            raise Exception('Authentication failed')
+
+        d = build_authenticator.authenticate(name, password)
+        d.addCallbacks(_success, _failure)
+        return DeferredResource(d)
+
 colormap = {
     'green': '#72ff75',
     }
@@ -310,12 +331,14 @@
 class StatusResourceBuild(HtmlResource):
     title = "Build"
 
-    def __init__(self, status, build, builderControl, buildControl):
+    def __init__(self, status, build, builderControl, buildControl,
+                 build_authenticator):
         HtmlResource.__init__(self)
         self.status = status
         self.build = build
         self.builderControl = builderControl
         self.control = buildControl
+        self.build_authenticator = build_authenticator
 
     def body(self, request):
         b = self.build
@@ -361,6 +384,9 @@
                 push the 'Stop' button</p>\n""" % stopURL
                 data += make_row("Your name:",
                                  "<input type='text' name='username' />")
+                if self.build_authenticator is not None:
+                    data += make_row("Password:",
+                        "<input type='password' name='password' />")
                 data += make_row("Reason for stopping build:",
                                  "<input type='text' name='comments' />")
                 data += """<input type="submit" value="Stop Builder" />
@@ -388,6 +414,9 @@
                      % rebuildURL)
             data += make_row("Your name:",
                              "<input type='text' name='username' />")
+            if self.build_authenticator is not None:
+                data += make_row("Password:",
+                    "<input type='password' name='password' />")
             data += make_row("Reason for re-running build:",
                              "<input type='text' name='comments' />")
             data += '<input type="submit" value="Rebuild" />\n'
@@ -425,6 +454,10 @@
         return data
 
     def stop(self, request):
+        return authenticate(self.build_authenticator, request,
+                            self._stop_authenticated)
+
+    def _stop_authenticated(self, request):
         log.msg("web stopBuild of build %s:%s" % \
                 (self.build.getBuilder().getName(),
                  self.build.getNumber()))
@@ -444,6 +477,10 @@
         return DeferredResource(d)
 
     def rebuild(self, request):
+        return authenticate(self.build_authenticator, request,
+                            self._rebuild_authenticated)
+
+    def _rebuild_authenticated(self, request):
         log.msg("web rebuild of build %s:%s" % \
                 (self.build.getBuilder().getName(),
                  self.build.getNumber()))
@@ -484,12 +521,13 @@
 # $builder
 class StatusResourceBuilder(HtmlResource):
 
-    def __init__(self, status, builder, control):
+    def __init__(self, status, builder, control, build_authenticator):
         HtmlResource.__init__(self)
         self.status = status
         self.title = builder.getName() + " Builder"
         self.builder = builder
         self.control = control
+        self.build_authenticator = build_authenticator
 
     def body(self, request):
         b = self.builder
@@ -522,14 +560,19 @@
         if self.control is not None and connected_slaves:
             forceURL = urllib.quote(request.childLink("force"))
             data += (
-                """
-                <form action='%(forceURL)s' class='command forcebuild'>
+                ("""
+                <form method='post' action='%(forceURL)s' class='command forcebuild'>
                 <p>To force a build, fill out the following fields and
                 push the 'Force Build' button</p>"""
-                + make_row("Your name:",
-                           "<input type='text' name='username' />")
-                + make_row("Reason for build:",
-                           "<input type='text' name='comments' />")
+                % {"forceURL": forceURL})
+                + make_row("Username:",
+                           "<input type='text' name='username' />"))
+            if self.build_authenticator is not None:
+                data += make_row("Password:",
+                                 "<input type='password' name='password' />")
+            data += (
+                make_row("Reason for build:",
+                         "<input type='text' name='comments' />")
                 + make_row("Branch to build:",
                            "<input type='text' name='branch' />")
                 + make_row("Revision to build:",
@@ -537,7 +580,7 @@
                 + """
                 <input type='submit' value='Force Build' />
                 </form>
-                """) % {"forceURL": forceURL}
+                """)
         elif self.control is not None:
             data += """
             <p>All buildslaves appear to be offline, so it's not possible
@@ -557,6 +600,10 @@
         return data
 
     def force(self, request):
+        return authenticate(self.build_authenticator, request,
+                            self._force_authenticated)
+
+    def _force_authenticated(self, request):
         name = request.args.get("username", ["<unknown>"])[0]
         reason = request.args.get("comments", ["<no reason specified>"])[0]
         branch = request.args.get("branch", [""])[0]
@@ -637,7 +684,8 @@
                 if self.control:
                     control = self.control.getBuild(num)
                 return StatusResourceBuild(self.status, build,
-                                           self.control, control)
+                                           self.control, control,
+                                           self.build_authenticator)
             else:
                 return NoResource("No such build '%d'" % num)
         return NoResource("really weird URL %s" % path)
@@ -1548,7 +1596,8 @@
     control = None
     favicon = None
 
-    def __init__(self, status, control, changemaster, categories, css):
+    def __init__(self, status, control, changemaster, categories, css,
+                 build_authenticator):
         """
         @type  status:       L{buildbot.status.builder.Status}
         @type  control:      L{buildbot.master.Control}
@@ -1560,6 +1609,7 @@
         self.changemaster = changemaster
         self.categories = categories
         self.css = css
+        self.build_authenticator = build_authenticator
         waterfall = WaterfallStatusResource(self.status, changemaster,
                                             categories, css)
         self.putChild("", waterfall)
@@ -1583,7 +1633,8 @@
             control = None
             if self.control:
                 control = self.control.getBuilder(path)
-            return StatusResourceBuilder(self.status, builder, control)
+            return StatusResourceBuilder(self.status, builder, control,
+                                         self.build_authenticator)
 
         return NoResource("No such Builder '%s'" % path)
 
@@ -1636,7 +1687,8 @@
                      "categories", "css", "favicon"]
 
     def __init__(self, http_port=None, distrib_port=None, allowForce=True,
-                 categories=None, css=buildbot_css, favicon=buildbot_icon):
+                 categories=None, css=buildbot_css, favicon=buildbot_icon,
+                 build_authenticator=None):
         """To have the buildbot run its own web server, pass a port number to
         C{http_port}. To have it run a web.distrib server
 
@@ -1702,6 +1754,7 @@
         self.categories = categories
         self.css = css
         self.favicon = favicon
+        self.build_authenticator = build_authenticator
 
     def __repr__(self):
         if self.http_port is None:
@@ -1726,7 +1779,7 @@
             control = None
         change_svc = self.parent.change_svc
         sr = StatusResource(status, control, change_svc, self.categories,
-                            self.css)
+                            self.css, self.build_authenticator)
         sr.favicon = self.favicon
         self.site = server.Site(sr)
 

