Ticket #482: buildbot-auth.patch
| File buildbot-auth.patch, 11.3 KB (added by ipv6guru, 3 years ago) |
|---|
-
buildbot/ldapauth.py
diff -Nru buildbot-0.7.2/buildbot/ldapauth.py buildbot-0.7.2-auth/buildbot/ldapauth.py
old new 1 #!/usr/bin/env python 2 3 from twisted.internet import reactor, defer 4 from ldaptor.protocols.ldap import ldapclient, ldapsyntax, ldapconnector, \ 5 distinguishedname 6 from ldaptor.protocols import pureldap 7 8 class LDAPAuthenticationSource(object): 9 def __init__(self, base_dn, hostname=None, attr='uid', bind_dn='', 10 bind_pw='', filter=pureldap.LDAPFilter_present('cn')): 11 self.hostname = hostname 12 self.base_dn = base_dn 13 self.attr = attr 14 self.bind_dn = bind_dn 15 self.bind_pw = bind_pw 16 self.filter = filter 17 18 def authenticate(self, username, password): 19 """Returns a deferred that will succeed or fail with the auth.""" 20 c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) 21 return c.connect(self.base_dn 22 ).addCallback(self._connected, username, password) 23 24 def _connected(self, cli, username, password): 25 # Bind a first time before issuing our query. 26 return cli.bind(self.bind_dn, self.bind_pw 27 ).addCallback(self._query_bound, username, password, cli) 28 29 def _query_bound(self, dummy, username, password, cli): 30 # Then issue a search for the uid we want. 31 entries = [] 32 baseEntry = ldapsyntax.LDAPEntry(cli, self.base_dn) 33 return baseEntry.search( 34 # This is stupid verbose, but at least it escapes properly. 35 filterObject=pureldap.LDAPFilter_and([ 36 pureldap.LDAPFilter_equalityMatch( 37 attributeDesc=pureldap.LDAPAttributeDescription(self.attr), 38 assertionValue=pureldap.LDAPAssertionValue(username) 39 ), 40 self.filter, 41 ]), 42 callback=entries.append 43 ).addCallback(self._search_end, password, entries) 44 45 def _search_end(self, dummy, password, entries): 46 n_entries = len(entries) 47 if n_entries == 0: 48 raise Exception('No search results!') 49 elif n_entries > 1: 50 raise Exception('%d search results for unique entry!' % n_entries) 51 else: 52 # The password matches if we can bind as this DN with it. 53 return entries[0].bind(password) -
buildbot/status/html.py
diff -Nru buildbot-0.7.2/buildbot/status/html.py buildbot-0.7.2-auth/buildbot/status/html.py
old new 58 58 label = html.escape(label) 59 59 return ROW_TEMPLATE % {"label": label, "field": field} 60 60 61 def authenticate(build_authenticator, request, nextfunc): 62 if build_authenticator is None: 63 # We assume they're authorized. 64 return nextfunc(request) 65 else: 66 # We actually check. 67 name = request.args.get("username", [None])[0] 68 password = request.args.get("password", [None])[0] 69 70 def _success(entry): 71 log.msg('Successful authentication as %r' % name) 72 return nextfunc(request) 73 74 def _failure(f): 75 log.msg('Failed authentication as %r' % name, f) 76 raise Exception('Authentication failed') 77 78 d = build_authenticator.authenticate(name, password) 79 d.addCallbacks(_success, _failure) 80 return DeferredResource(d) 81 61 82 colormap = { 62 83 'green': '#72ff75', 63 84 } … … 310 331 class StatusResourceBuild(HtmlResource): 311 332 title = "Build" 312 333 313 def __init__(self, status, build, builderControl, buildControl): 334 def __init__(self, status, build, builderControl, buildControl, 335 build_authenticator): 314 336 HtmlResource.__init__(self) 315 337 self.status = status 316 338 self.build = build 317 339 self.builderControl = builderControl 318 340 self.control = buildControl 341 self.build_authenticator = build_authenticator 319 342 320 343 def body(self, request): 321 344 b = self.build … … 361 384 push the 'Stop' button</p>\n""" % stopURL 362 385 data += make_row("Your name:", 363 386 "<input type='text' name='username' />") 387 if self.build_authenticator is not None: 388 data += make_row("Password:", 389 "<input type='password' name='password' />") 364 390 data += make_row("Reason for stopping build:", 365 391 "<input type='text' name='comments' />") 366 392 data += """<input type="submit" value="Stop Builder" /> … … 388 414 % rebuildURL) 389 415 data += make_row("Your name:", 390 416 "<input type='text' name='username' />") 417 if self.build_authenticator is not None: 418 data += make_row("Password:", 419 "<input type='password' name='password' />") 391 420 data += make_row("Reason for re-running build:", 392 421 "<input type='text' name='comments' />") 393 422 data += '<input type="submit" value="Rebuild" />\n' … … 425 454 return data 426 455 427 456 def stop(self, request): 457 return authenticate(self.build_authenticator, request, 458 self._stop_authenticated) 459 460 def _stop_authenticated(self, request): 428 461 log.msg("web stopBuild of build %s:%s" % \ 429 462 (self.build.getBuilder().getName(), 430 463 self.build.getNumber())) … … 444 477 return DeferredResource(d) 445 478 446 479 def rebuild(self, request): 480 return authenticate(self.build_authenticator, request, 481 self._rebuild_authenticated) 482 483 def _rebuild_authenticated(self, request): 447 484 log.msg("web rebuild of build %s:%s" % \ 448 485 (self.build.getBuilder().getName(), 449 486 self.build.getNumber())) … … 484 521 # $builder 485 522 class StatusResourceBuilder(HtmlResource): 486 523 487 def __init__(self, status, builder, control ):524 def __init__(self, status, builder, control, build_authenticator): 488 525 HtmlResource.__init__(self) 489 526 self.status = status 490 527 self.title = builder.getName() + " Builder" 491 528 self.builder = builder 492 529 self.control = control 530 self.build_authenticator = build_authenticator 493 531 494 532 def body(self, request): 495 533 b = self.builder … … 522 560 if self.control is not None and connected_slaves: 523 561 forceURL = urllib.quote(request.childLink("force")) 524 562 data += ( 525 """526 <form action='%(forceURL)s' class='command forcebuild'>563 (""" 564 <form method='post' action='%(forceURL)s' class='command forcebuild'> 527 565 <p>To force a build, fill out the following fields and 528 566 push the 'Force Build' button</p>""" 529 + make_row("Your name:", 530 "<input type='text' name='username' />") 531 + make_row("Reason for build:", 532 "<input type='text' name='comments' />") 567 % {"forceURL": forceURL}) 568 + make_row("Username:", 569 "<input type='text' name='username' />")) 570 if self.build_authenticator is not None: 571 data += make_row("Password:", 572 "<input type='password' name='password' />") 573 data += ( 574 make_row("Reason for build:", 575 "<input type='text' name='comments' />") 533 576 + make_row("Branch to build:", 534 577 "<input type='text' name='branch' />") 535 578 + make_row("Revision to build:", … … 537 580 + """ 538 581 <input type='submit' value='Force Build' /> 539 582 </form> 540 """) % {"forceURL": forceURL}583 """) 541 584 elif self.control is not None: 542 585 data += """ 543 586 <p>All buildslaves appear to be offline, so it's not possible … … 557 600 return data 558 601 559 602 def force(self, request): 603 return authenticate(self.build_authenticator, request, 604 self._force_authenticated) 605 606 def _force_authenticated(self, request): 560 607 name = request.args.get("username", ["<unknown>"])[0] 561 608 reason = request.args.get("comments", ["<no reason specified>"])[0] 562 609 branch = request.args.get("branch", [""])[0] … … 637 684 if self.control: 638 685 control = self.control.getBuild(num) 639 686 return StatusResourceBuild(self.status, build, 640 self.control, control) 687 self.control, control, 688 self.build_authenticator) 641 689 else: 642 690 return NoResource("No such build '%d'" % num) 643 691 return NoResource("really weird URL %s" % path) … … 1548 1596 control = None 1549 1597 favicon = None 1550 1598 1551 def __init__(self, status, control, changemaster, categories, css): 1599 def __init__(self, status, control, changemaster, categories, css, 1600 build_authenticator): 1552 1601 """ 1553 1602 @type status: L{buildbot.status.builder.Status} 1554 1603 @type control: L{buildbot.master.Control} … … 1560 1609 self.changemaster = changemaster 1561 1610 self.categories = categories 1562 1611 self.css = css 1612 self.build_authenticator = build_authenticator 1563 1613 waterfall = WaterfallStatusResource(self.status, changemaster, 1564 1614 categories, css) 1565 1615 self.putChild("", waterfall) … … 1583 1633 control = None 1584 1634 if self.control: 1585 1635 control = self.control.getBuilder(path) 1586 return StatusResourceBuilder(self.status, builder, control) 1636 return StatusResourceBuilder(self.status, builder, control, 1637 self.build_authenticator) 1587 1638 1588 1639 return NoResource("No such Builder '%s'" % path) 1589 1640 … … 1636 1687 "categories", "css", "favicon"] 1637 1688 1638 1689 def __init__(self, http_port=None, distrib_port=None, allowForce=True, 1639 categories=None, css=buildbot_css, favicon=buildbot_icon): 1690 categories=None, css=buildbot_css, favicon=buildbot_icon, 1691 build_authenticator=None): 1640 1692 """To have the buildbot run its own web server, pass a port number to 1641 1693 C{http_port}. To have it run a web.distrib server 1642 1694 … … 1702 1754 self.categories = categories 1703 1755 self.css = css 1704 1756 self.favicon = favicon 1757 self.build_authenticator = build_authenticator 1705 1758 1706 1759 def __repr__(self): 1707 1760 if self.http_port is None: … … 1726 1779 control = None 1727 1780 change_svc = self.parent.change_svc 1728 1781 sr = StatusResource(status, control, change_svc, self.categories, 1729 self.css )1782 self.css, self.build_authenticator) 1730 1783 sr.favicon = self.favicon 1731 1784 self.site = server.Site(sr) 1732 1785
![[Buildbot Logo]](/chrome/site/header-text-transparent.png)