def test_advertiseAction_auth_authenticated(self): z = Authz(auth=StubAuth('uu'),stopBuild = 'auth') r = StubRequest('uu','aa') d = z.login(r) def check(c): assert z.advertiseAction('stopBuild',r) d.addCallback(check)
def test_actionAllowed_Defaults(self): "by default, nothing is allowed" z = Authz() self.failedActions = [] self.dl = [] for a in Authz.knownActions: md = z.actionAllowed(a, StubRequest('foo', 'bar')) def check(res): if res: self.failedActions.append(a) return md.addCallback(check) self.dl.append(md) d = defer.DeferredList(self.dl) def check_failed(_): if self.failedActions: raise unittest.FailTest( "action(s) %s do not default to False" % (self.failedActions, )) d.addCallback(check_failed) return d
def test_actionAllowed_AuthCallableFalse(self): def myAuthzFn(*args): return False z = Authz(auth=StubAuth('uu'), stopBuild=myAuthzFn) self.assertFalse(z.actionAllowed('stopBuild', StubRequest('uu', 'shh')))
def test_actionAllowed_Defaults(self): "by default, nothing is allowed" z = Authz() self.failedActions = [] self.dl = [] for a in Authz.knownActions: md = z.actionAllowed(a, StubRequest('foo', 'bar')) def check(res, expected): if res != expected: self.failedActions.append((a, expected)) return md.addCallback(check, a == 'view') self.dl.append(md) d = defer.DeferredList(self.dl) def check_failed(_): if self.failedActions: msgs = '; '.join('%s action authz did not default to %s' % f for f in self.failedActions) raise unittest.FailTest(msgs) d.addCallback(check_failed) return d
def test_authenticated_True(self): z = Authz(auth=StubAuth('uu'), forceBuild = True) r = StubRequest('uu','aa') d = z.login(r) @d.addCallback def check(c): assert z.authenticated(r) return d
def test_advertiseAction_callable(self): z = Authz(auth=StubAuth('uu'), stopAllBuilds = lambda u : False) r = StubRequest('uu','aa') d = z.login(r) @d.addCallback def check(c): assert z.advertiseAction('stopAllBuilds',r) return d
def test_actionAllowed_AuthCallable(self): myargs = [] def myAuthzFn(*args): myargs.extend(args) z = Authz(auth=StubAuth('uu'), stopBuild=myAuthzFn) z.actionAllowed('stopBuild', StubRequest('uu', 'shh'), 'arg', 'arg2') self.assertEqual(myargs, ['uu', 'arg', 'arg2'])
def test_actionAllowed_Positive(self): "'True' should always permit access" z = Authz(forceBuild=True) d = z.actionAllowed('forceBuild', StubRequest('foo', 'bar')) def check(res): self.assertEqual(res, True) d.addCallback(check) return d
def test_actionAllowed_AuthPositive(self): z = Authz(auth=StubAuth('jrobinson'), stopBuild='auth') d = z.actionAllowed('stopBuild', StubRequest('jrobinson', 'bar')) def check(res): self.assertEqual(res, True) d.addCallback(check) return d
def test_actionAllowed_AuthNegative(self): z = Authz(auth=StubAuth('jrobinson'), stopBuild='auth') d = z.actionAllowed('stopBuild', StubRequest('apeterson', 'bar')) def check(res): self.assertEqual(res, False) d.addCallback(check) return d
def test_actionAllowed_AuthPositive(self): z = Authz(auth=StubAuth("jrobinson"), stopBuild="auth") d = z.actionAllowed("stopBuild", StubRequest("jrobinson", "bar")) def check(res): self.assertEqual(res, True) d.addCallback(check) return d
def test_actionAllowed_AuthCallableFalse(self): def myAuthzFn(*args): return False z = Authz(auth=StubAuth('uu'), stopBuild=myAuthzFn) d = z.actionAllowed('stopBuild', StubRequest('uu', 'shh')) def check(res): self.assertEqual(res, False) d.addCallback(check) return d
def test_actionAllowed_Defaults(self): "by default, nothing is allowed" z = Authz() failedActions = [] for a in Authz.knownActions: if z.actionAllowed(a, StubRequest('foo', 'bar')): failedActions.append(a) if failedActions: raise unittest.FailTest("action(s) %s do not default to False" % (failedActions, ))
def test_actionAllowed_Defaults(self): "by default, nothing is allowed" z = Authz() failedActions = [] for a in Authz.knownActions: if z.actionAllowed(a, StubRequest('foo', 'bar')): failedActions.append(a) if failedActions: raise unittest.FailTest("action(s) %s do not default to False" % (failedActions,))
def test_actionAllowed_AuthCallable(self): myargs = [] def myAuthzFn(*args): myargs.extend(args) z = Authz(auth=StubAuth('uu'), stopBuild=myAuthzFn) d = z.actionAllowed('stopBuild', StubRequest('uu', 'shh'), 'arg', 'arg2') def check(res): self.assertEqual(myargs, ['uu', 'arg', 'arg2']) d.addCallback(check) return d
def test_actionAllowed_AuthCallableTrue(self): def myAuthzFn(*args): return True z = Authz(auth=StubAuth("uu"), stopBuild=myAuthzFn) d = z.actionAllowed("stopBuild", StubRequest("uu", "shh")) def check(res): self.assertEqual(res, True) d.addCallback(check) return d
def test_actionAllowed_AuthCallable(self): myargs = [] def myAuthzFn(*args): myargs.extend(args) z = Authz(auth=StubAuth("uu"), stopBuild=myAuthzFn) d = z.actionAllowed("stopBuild", StubRequest("uu", "shh"), "arg", "arg2") def check(res): self.assertEqual(myargs, ["uu", "arg", "arg2"]) d.addCallback(check) return d
def test_actionAllowed_Defaults(self): "by default, nothing is allowed" z = Authz() self.failedActions = [] self.dl = [] for a in Authz.knownActions: md = z.actionAllowed(a, StubRequest('foo', 'bar')) def check(res): if res: self.failedActions.append(a) return md.addCallback(check) self.dl.append(md) d = defer.DeferredList(self.dl) def check_failed(_): if self.failedActions: raise unittest.FailTest("action(s) %s do not default to False" % (self.failedActions,)) d.addCallback(check_failed) return d
def get_status(options): """ Returns a list of status targets for the build master. """ # Load the users that are allowed to perform authenticated # actions from the configuration auth_users = [] if options.http_users is not None: for pair in options.http_users.split(","): user, password = pair.split(":") auth_users.append((user, password)) # Setup the rules for who can do what to the WebStatus authz = Authz(auth=BasicAuth(auth_users), gracefulShutdown=False, forceBuild='auth', forceAllBuilds='auth', pingBuilder=True, stopBuild='auth', stopAllBuilds='auth', cancelPendingBuild='auth', stopChange='auth', cleanShutdown=False) # The web status gives us the nice web interface to our buildbot web_status = html.WebStatus(http_port=options.web_port, authz=authz, order_console_by_time=True, change_hook_dialects=dict(github=True)) # Hook up an IRC bot into our channel irc = words.IRC("irc.freenode.net", "vagrant-ci", channels=[{ "channel": "#vagrant" }], notify_events={ 'exception': 1, 'successToFailure': 1, 'failureToSuccess': 1 }) return [web_status, irc]
def test_needAuthForm_auth(self): z = Authz(stopBuild = 'auth') assert z.needAuthForm('stopBuild')
def test_getPassword_http_missing(self): z = Authz(useHttpHeader = True) assert z.getPassword(StubRequest('foo', 'bar')) == None
def test_getPassword_request(self): z = Authz() assert z.getPassword(StubRequest('foo', 'bar')) == 'bar'
def test_authenticated_http_False(self): z = Authz(useHttpHeader = True) assert not z.authenticated(StubRequest())
def test_getUsername_http(self): z = Authz(useHttpHeader = True) assert z.getUsername(StubHttpAuthRequest('foo', 'bar')) == 'foo'
def test_needUserForm_http_True(self): z = Authz(useHttpHeader = True, forceBuild = True) assert not z.needUserForm('forceBuild')
def test_authenticated_False(self): z = Authz(forceBuild = False) assert not z.authenticated(StubRequest())
def test_needAuthForm_callable(self): z = Authz(stopAllBuilds = lambda u : False) assert z.needAuthForm('stopAllBuilds')
def test_needAuthForm_False(self): z = Authz(forceBuild = False) assert not z.needAuthForm('forceBuild')
def test_needAuthForm_True(self): z = Authz(forceAllBuilds = True) assert not z.needAuthForm('forceAllBuilds')
def test_getUsername_request(self): z = Authz() assert z.getUsername(StubRequest('foo', 'bar')) == 'foo'
def test_needAuthForm_invalidAction(self): z = Authz() self.assertRaises(KeyError, z.needAuthForm, 'someRandomAction')
def __init__(self, http_port=None, distrib_port=None, allowForce=None, public_html="public_html", site=None, numbuilds=20, num_events=200, num_events_max=None, auth=None, order_console_by_time=False, changecommentlink=None, revlink=None, projects=None, repositories=None, authz=None, logRotateLength=None, maxRotatedFiles=None, change_hook_dialects={}, provide_feeds=None, jinja_loaders=None): """Run a web server that provides Buildbot status. @type http_port: int or L{twisted.application.strports} string @param http_port: a strports specification describing which port the buildbot should use for its web server, with the Waterfall display as the root page. For backwards compatibility this can also be an int. Use 'tcp:8000' to listen on that port, or 'tcp:12345:interface=127.0.0.1' if you only want local processes to connect to it (perhaps because you are using an HTTP reverse proxy to make the buildbot available to the outside world, and do not want to make the raw port visible). @type distrib_port: int or L{twisted.application.strports} string @param distrib_port: Use this if you want to publish the Waterfall page using web.distrib instead. The most common case is to provide a string that is an absolute pathname to the unix socket on which the publisher should listen (C{os.path.expanduser(~/.twistd-web-pb)} will match the default settings of a standard twisted.web 'personal web server'). Another possibility is to pass an integer, which means the publisher should listen on a TCP socket, allowing the web server to be on a different machine entirely. Both forms are provided for backwards compatibility; the preferred form is a strports specification like 'unix:/home/buildbot/.twistd-web-pb'. Providing a non-absolute pathname will probably confuse the strports parser. @param allowForce: deprecated; use authz instead @param auth: deprecated; use with authz @param authz: a buildbot.status.web.authz.Authz instance giving the authorization parameters for this view @param public_html: the path to the public_html directory for this display, either absolute or relative to the basedir. The default is 'public_html', which selects BASEDIR/public_html. @type site: None or L{twisted.web.server.Site} @param site: Use this if you want to define your own object instead of using the default.` @type numbuilds: int @param numbuilds: Default number of entries in lists at the /one_line_per_build and /builders/FOO URLs. This default can be overriden both programatically --- by passing the equally named argument to constructors of OneLinePerBuildOneBuilder and OneLinePerBuild --- and via the UI, by tacking ?numbuilds=xy onto the URL. @type num_events: int @param num_events: Default number of events to show in the waterfall. @type num_events_max: int @param num_events_max: The maximum number of events that are allowed to be shown in the waterfall. The default value of C{None} will disable this check @type auth: a L{status.web.auth.IAuth} or C{None} @param auth: an object that performs authentication to restrict access to the C{allowForce} features. Ignored if C{allowForce} is not C{True}. If C{auth} is C{None}, people can force or stop builds without auth. @type order_console_by_time: bool @param order_console_by_time: Whether to order changes (commits) in the console view according to the time they were created (for VCS like Git) or according to their integer revision numbers (for VCS like SVN). @type changecommentlink: callable, dict, tuple (2 or 3 strings) or C{None} @param changecommentlink: adds links to ticket/bug ids in change comments, see buildbot.status.web.base.changecommentlink for details @type revlink: callable, dict, string or C{None} @param revlink: decorations revision ids with links to a web-view, see buildbot.status.web.base.revlink for details @type projects: callable, dict or c{None} @param projects: maps project identifiers to URLs, so that any project listed is automatically decorated with a link to it's front page. see buildbot.status.web.base.dictlink for details @type repositories: callable, dict or c{None} @param repositories: maps repository identifiers to URLs, so that any project listed is automatically decorated with a link to it's web view. see buildbot.status.web.base.dictlink for details @type logRotateLength: None or int @param logRotateLength: file size at which the http.log is rotated/reset. If not set, the value set in the buildbot.tac will be used, falling back to the BuildMaster's default value (1 Mb). @type maxRotatedFiles: None or int @param maxRotatedFiles: number of old http.log files to keep during log rotation. If not set, the value set in the buildbot.tac will be used, falling back to the BuildMaster's default value (10 files). @type change_hook_dialects: None or dict @param change_hook_dialects: If empty, disables change_hook support, otherwise whitelists valid dialects. In the format of {"dialect1": "Option1", "dialect2", None} Where the values are options that will be passed to the dialect To enable the DEFAULT handler, use a key of DEFAULT @type provide_feeds: None or list @param provide_feeds: If empty, provides atom, json, and rss feeds. Otherwise, a dictionary of strings of the type of feeds provided. Current possibilities are "atom", "json", and "rss" @type jinja_loaders: None or list @param jinja_loaders: If not empty, a list of additional Jinja2 loader objects to search for templates. """ service.MultiService.__init__(self) if type(http_port) is int: http_port = "tcp:%d" % http_port self.http_port = http_port if distrib_port is not None: if type(distrib_port) is int: distrib_port = "tcp:%d" % distrib_port if distrib_port[0] in "/~.": # pathnames distrib_port = "unix:%s" % distrib_port self.distrib_port = distrib_port self.num_events = num_events if num_events_max: if num_events_max < num_events: config.error("num_events_max must be greater than num_events") self.num_events_max = num_events_max self.public_html = public_html # make up an authz if allowForce was given if authz: if allowForce is not None: config.error("cannot use both allowForce and authz parameters") if auth: config.error( "cannot use both auth and authz parameters (pass " + "auth as an Authz parameter)") else: # invent an authz if allowForce and auth: authz = Authz(auth=auth, default_action="auth") elif allowForce: authz = Authz(default_action=True) else: if auth: log.msg( "Warning: Ignoring authentication. Search for 'authorization'" " in the manual") authz = Authz() # no authorization for anything self.authz = authz self.orderConsoleByTime = order_console_by_time # If we were given a site object, go ahead and use it. (if not, we add one later) self.site = site # store the log settings until we create the site object self.logRotateLength = logRotateLength self.maxRotatedFiles = maxRotatedFiles # create the web site page structure self.childrenToBeAdded = {} self.setupUsualPages(numbuilds=numbuilds, num_events=num_events, num_events_max=num_events_max) self.revlink = revlink self.changecommentlink = changecommentlink self.repositories = repositories self.projects = projects # keep track of cached connections so we can break them when we shut # down. See ticket #102 for more details. self.channels = weakref.WeakKeyDictionary() # do we want to allow change_hook self.change_hook_dialects = {} if change_hook_dialects: self.change_hook_dialects = change_hook_dialects self.putChild( "change_hook", ChangeHookResource(dialects=self.change_hook_dialects)) # Set default feeds if provide_feeds is None: self.provide_feeds = ["atom", "json", "rss"] else: self.provide_feeds = provide_feeds self.jinja_loaders = jinja_loaders
def test_advertiseAction_auth(self): z = Authz(stopBuild = 'auth') assert not z.advertiseAction('stopBuild',StubRequest())
def AutoSetupMaster(c, active_master, mail_notifier=False, mail_notifier_mode=None, public_html=None, templates=None, order_console_by_time=False, tagComparator=None, customEndpoints=None, enable_http_status_push=False, console_repo_filter=None, console_builder_filter=None, web_template_globals=None): """Add common settings and status services to a master. If you wonder what all these mean, PLEASE go check the official doc! http://buildbot.net/buildbot/docs/0.7.12/ or http://buildbot.net/buildbot/docs/latest/full.html - Default number of logs to keep - WebStatus and MailNotifier - Debug ssh port. Just add a file named .manhole beside master.cfg and simply include one line containing 'port = 10101', then you can 'ssh localhost -p' and you can access your buildbot from the inside.""" if active_master.in_production and not active_master.is_production_host: log.err('ERROR: Trying to start the master on the wrong host.') log.err('ERROR: This machine is %s, expected %s.' % (active_master.current_host, active_master.master_host)) raise WrongHostException c['slavePortnum'] = active_master.slave_port c['projectName'] = active_master.project_name c['projectURL'] = config.Master.project_url c['properties'] = {'mastername': GetMastername()} if 'buildbotURL' in c: c['properties']['buildbotURL'] = c['buildbotURL'] # 'status' is a list of Status Targets. The results of each build will be # pushed to these targets. buildbot/status/*.py has a variety to choose from, # including web pages, email senders, and IRC bots. c.setdefault('status', []) if mail_notifier: # pylint: disable=E1101 c['status'].append( mail.MailNotifier(fromaddr=active_master.from_address, mode=mail_notifier_mode or 'problem', relayhost=config.Master.smtp, lookup=FilterDomain())) # Add in the pubsub pusher, which pushes all status updates to a pubsub # topic. This will not run unless is_production_host is set to True. # This will fail on a production host if it cannot find the service # account file. pubsub_pusher = pubsub_json_status_push.StatusPush.CreateStatusPush( activeMaster=active_master) if pubsub_pusher: c['status'].append(pubsub_pusher) else: log.msg('Pubsub not enabled.') # For all production masters, notify our health-monitoring webapp. if enable_http_status_push: blacklist = ( 'buildETAUpdate', #'buildFinished', 'buildStarted', 'buildedRemoved', 'builderAdded', 'builderChangedState', 'buildsetSubmitted', 'changeAdded', 'logFinished', 'logStarted', 'requestCancelled', 'requestSubmitted', 'slaveConnected', 'slaveDisconnected', 'stepETAUpdate', 'stepFinished', 'stepStarted', 'stepText2Changed', 'stepTextChanged', ) c['status'].append( HttpStatusPush( 'https://chromium-build-logs.appspot.com/status_receiver', blackList=blacklist)) # Enable Chrome Build Extract status push if configured. This requires the # configuration file to be defined and valid for this master. status_push = None try: status_push = cbe_json_status_push.StatusPush.load( active_master, pushInterval=30, # Push every 30 seconds. ) except cbe_json_status_push.ConfigError as e: log.err( None, 'Failed to load configuration; not installing CBE status ' 'push: %s' % (e.message, )) if status_push: # A status push configuration was identified. c['status'].append(status_push) kwargs = {} if public_html: kwargs['public_html'] = public_html kwargs['order_console_by_time'] = order_console_by_time # In Buildbot 0.8.4p1, pass provide_feeds as a list to signal what extra # services Buildbot should be able to provide over HTTP. if buildbot.version == '0.8.4p1': kwargs['provide_feeds'] = ['json'] if active_master.master_port: # Actions we want to allow must be explicitly listed here. # Deliberately omitted are: # - gracefulShutdown # - cleanShutdown authz = Authz(forceBuild=True, forceAllBuilds=True, pingBuilder=True, stopBuild=True, stopAllBuilds=True, cancelPendingBuild=True) c['status'].append( CreateWebStatus(active_master.master_port, tagComparator=tagComparator, customEndpoints=customEndpoints, authz=authz, num_events_max=3000, templates=templates, console_repo_filter=console_repo_filter, console_builder_filter=console_builder_filter, web_template_globals=web_template_globals, **kwargs)) if active_master.master_port_alt: c['status'].append( CreateWebStatus(active_master.master_port_alt, tagComparator=tagComparator, customEndpoints=customEndpoints, num_events_max=3000, templates=templates, console_repo_filter=console_repo_filter, console_builder_filter=console_builder_filter, web_template_globals=web_template_globals, **kwargs)) # Add a status logger and a ts_mon flushing receiver. c['status'].append(status_logger.StatusEventLogger()) c['status'].append(monitoring_status_receiver.MonitoringStatusReceiver()) # Keep last build logs, the default is too low. c['buildHorizon'] = 1000 c['logHorizon'] = 500 # Must be at least 2x the number of slaves. c['eventHorizon'] = 200 # Tune cache sizes to speed up web UI. c['caches'] = { 'BuildRequests': 1000, 'Changes': 1000, 'SourceStamps': 1000, 'chdicts': 1000, 'ssdicts': 1000, } # Must be at least 2x the number of on-going builds. c['buildCacheSize'] = 200 # See http://buildbot.net/buildbot/docs/0.8.1/Debug-Options.html for more # details. if os.path.isfile('.manhole'): try: from buildbot import manhole except ImportError: log.msg( 'Using manhole has an implicit dependency on Crypto.Cipher. You ' 'need to install it manually:\n' ' sudo apt-get install python-crypto\n' 'on ubuntu or run:\n' ' pip install --user pycrypto\n' ' pip install --user pyasn1\n') raise # If 'port' is defined, it uses the same valid keys as the current user. values = {} execfile('.manhole', values) if 'debugPassword' in values: c['debugPassword'] = values['debugPassword'] interface = 'tcp:%s:interface=127.0.0.1' % values.get('port', 0) if 'port' in values and 'user' in values and 'password' in values: c['manhole'] = manhole.PasswordManhole(interface, values['user'], values['password']) elif 'port' in values: c['manhole'] = manhole.AuthorizedKeysManhole( interface, os.path.expanduser("~/.ssh/authorized_keys")) if active_master.buildbucket_bucket and active_master.service_account_path: SetupBuildbucket(c, active_master) SetMasterProcessName()
def test_needUserForm_Ture(self): z = Authz(forceBuild = True) assert z.needUserForm('forceBuild')
status = [] from buildbot.status import html from buildbot.status.web.authz import Authz from buildbot.status.web.auth import BasicAuth from buildbot import revlinks users = [('dev', 'bbot!')] # it's not *that* secret.. authz = Authz( auth=BasicAuth(users), forceBuild='auth', ) revlink = revlinks.RevlinkMatch([r'git://github.com/(.*)/(.*)'], r'https://github.com/\1/\2/commit/%s') status.append( html.WebStatus( http_port=8010, authz=authz, order_console_by_time=True, change_hook_dialects={'github': True}, )) # from buildbot.status import words # status.append(words.IRC(host="irc.oftc.net", nick="institute-bb", # notify_events={ # 'successToFailure' : 1, # 'failureToSuccess' : 1, # }, # channels=["#institute"]))
def test_needUserForm_False(self): z = Authz(forceBuild = False) assert not z.needUserForm('forceBuild')
from buildbot.status import html from buildbot.status import words from buildbot.status.web.auth import BasicAuth from buildbot.status.web.authz import Authz status = [] users = [('dev', 'bbot!') # it's not *that* secret.. ] authz = Authz(auth=BasicAuth(users), forceBuild='auth', forceAllBuilds='auth', gracefulShutdown='auth') status.append( html.WebStatus( # localhost is not available in the jail http_port="tcp:8010:interface=192.168.80.239", authz=authz, order_console_by_time=True, revlink="http://github.com/buildbot/buildbot/commit/%s", changecommentlink=(r'\b#(\d+)\b', r'http://buildbot.net/trac/ticket/\1', r'Ticket \g<0>'), change_hook_dialects={'github': {}})) status.append( words.IRC(host="irc.freenode.net", nick="bb-meta", notify_events={
def test_authenticated_http_True(self): z = Authz(useHttpHeader = True) assert z.authenticated(StubHttpAuthRequest('foo', 'bar'))
def test_getPassword_http(self): z = Authz(useHttpHeader = True) assert z.getPassword(StubHttpAuthRequest('foo', 'bar')) == 'bar'
def test_advertiseAction_False(self): z = Authz(forceBuild = False) assert not z.advertiseAction('forceBuild',StubRequest())
def test_advertiseAction_True(self): z = Authz(forceAllBuilds = True) assert z.advertiseAction('forceAllBuilds',StubRequest())
def test_actionAllowed_invalidAction(self): z = Authz() self.assertRaises(KeyError, z.actionAllowed, 'someRandomAction', StubRequest('snow', 'foo'))
def test_advertiseAction_invalidAction(self): z = Authz() self.assertRaises(KeyError, z.advertiseAction, 'someRandomAction')