Skip to content

Commit

Permalink
Expose twisted.cred for change hook auth.
Browse files Browse the repository at this point in the history
Twisted provides a rich interface for defining authentication. Use it
for defining change hook authorization, rather than just one specific
bit of it.
  • Loading branch information
tomprince committed Jul 13, 2013
1 parent c7fc980 commit 3b1b6e6
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 12 deletions.
34 changes: 24 additions & 10 deletions master/buildbot/status/web/baseweb.py
Expand Up @@ -45,8 +45,10 @@
from buildbot.status.web.users import UsersResource
from buildbot.status.web.change_hook import ChangeHookResource
from twisted.cred.portal import IRealm, Portal
from twisted.cred import strcred
from twisted.cred.checkers import ICredentialsChecker
from twisted.cred.credentials import IUsernamePassword
from twisted.web import resource, guard
from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse

# this class contains the WebStatus class. Basic utilities are in base.py,
# and specific pages are each in their own module.
Expand Down Expand Up @@ -320,9 +322,23 @@ def __init__(self, http_port=None, distrib_port=None, allowForce=None,

# check for correctness of HTTP auth parameters
if change_hook_auth is not None:
if not isinstance(change_hook_auth, tuple) or len(change_hook_auth) != 2:
config.error("Invalid credentials for change_hook auth")
self.change_hook_auth = change_hook_auth
self.change_hook_auth = []
for checker in change_hook_auth:
if isinstance(checker, str):
try:
checker = strcred.makeChecker(checker)
except Exception, error:
config.error("Invalid change_hook checker description: %s" % (error,))
continue
elif not ICredentialsChecker.providedBy(checker):
config.error("change_hook checker doesn't provide ICredentialChecker: %r" % (checker,))
continue

if IUsernamePassword not in checker.credentialInterfaces:
config.error("change_hook checker doesn't support IUsernamePassword: %r" % (checker,))
self.change_hook_auth.append(checker)
else:
self.change_hook_auth = None

self.orderConsoleByTime = order_console_by_time

Expand Down Expand Up @@ -357,7 +373,8 @@ def __init__(self, http_port=None, distrib_port=None, allowForce=None,
self.change_hook_dialects = change_hook_dialects
resource_obj = ChangeHookResource(dialects=self.change_hook_dialects)
if self.change_hook_auth is not None:
resource_obj = self.setupProtectedResource(resource_obj)
resource_obj = self.setupProtectedResource(
resource_obj, self.change_hook_auth)
self.putChild("change_hook", resource_obj)

# Set default feeds
Expand All @@ -368,7 +385,7 @@ def __init__(self, http_port=None, distrib_port=None, allowForce=None,

self.jinja_loaders = jinja_loaders

def setupProtectedResource(self, resource_obj):
def setupProtectedResource(self, resource_obj, checkers):
class SimpleRealm(object):
"""
A realm which gives out L{ChangeHookResource} instances for authenticated
Expand All @@ -381,10 +398,7 @@ def requestAvatar(self, avatarId, mind, *interfaces):
return (resource.IResource, resource_obj, lambda: None)
raise NotImplementedError()

login, password = self.change_hook_auth
checker = InMemoryUsernamePasswordDatabaseDontUse()
checker.addUser(login, password)
portal = Portal(SimpleRealm(), [checker])
portal = Portal(SimpleRealm(), checkers)
credentialFactory = guard.BasicCredentialFactory('Protected area')
wrapper = guard.HTTPAuthSessionWrapper(portal, [credentialFactory])
return wrapper
Expand Down
8 changes: 6 additions & 2 deletions master/docs/manual/cfg-statustargets.rst
Expand Up @@ -730,10 +730,14 @@ useful in cases where you cannot expose the WebStatus for public consumption.
Anyone who can access the web status can "fake" a request from
GitHub, potentially causing the buildmaster to run arbitrary code.

To protect URL against unauthorized access you should use ``change_hook_auth`` option. ::
To protect URL against unauthorized access you should use ``change_hook_auth`` option ::

c['status'].append(html.WebStatus(..
change_hook_auth=('user', 'password')))
change_hook_auth=["file:changehook.passwd"]))

And create a file ``changehook.passwd`` ::

user:password

Then, create a GitHub service hook (see https://help.github.com/articles/post-receive-hooks) with a WebHook URL like ``http://user:password@builds.mycompany.com/bbot/change_hook/github``.

Expand Down

0 comments on commit 3b1b6e6

Please sign in to comment.