def test_set_tags(self): resource = Resource('wiki', 'TaggedPage') self.req.perm = PermissionCache(self.env, username='******') # Shouldn't raise an error with appropriate permission. self.tag_wp.set_resource_tags(self.req, resource, self.tags) self.tag_wp.set_resource_tags(self.req, resource, ['tag2']) cursor = self.db.cursor() # Check change records. cursor.execute( """ SELECT * FROM tags_change WHERE tagspace=%s AND name=%s ORDER by time DESC """, ('wiki', 'TaggedPage')) row = cursor.fetchone() self.assertEqual([v for v in row[-3:]], ['editor', 'tag1', 'tag2']) row = cursor.fetchone() self.assertEqual([v for v in row[-3:]], ['editor', '', 'tag1'])
def setUp(self): self.env = EnvironmentStub() self.env.config.set( 'trac', 'templates_dir', os.path.join(os.path.dirname(self.env.path), 'templates')) self.ticket_module = TicketModule(self.env) self.mimeview = Mimeview(self.env) self.req = Mock(base_path='/trac.cgi', path_info='', href=Href('/trac.cgi'), chrome={'logo': {}}, abs_href=Href('http://example.org/trac.cgi'), environ={}, perm=PermissionCache(self.env, '-'), authname='-', args={}, tz=None, locale='', session=None, form_token=None)
def _create_request(self, chrome): req = Request( { 'REQUEST_METHOD': 'GET', 'trac.base_url': self.env.abs_href(), }, lambda *args, **kwargs: None) req.arg_list = () req.args = {} req.authname = 'anonymous' req.session = FakeSession({'dateinfo': 'absolute'}) req.perm = PermissionCache(self.env, req.authname) req.href = req.abs_href req.callbacks.update({ 'chrome': chrome.prepare_request, 'tz': self._get_tz, 'locale': self._get_locale, 'lc_time': lambda req: 'iso8601', }) return req
def _update_tickets(self, tickets, changeset, comment, date): """Update the tickets with the given comment.""" perm = PermissionCache(self.env, changeset.author) for tkt_id, cmds in tickets.iteritems(): try: self.log.debug("Updating ticket #%d", tkt_id) ticket = [None] @self.env.with_transaction() def do_update(db): ticket[0] = Ticket(self.env, tkt_id, db) for cmd in cmds: cmd(ticket[0], changeset, perm(ticket[0].resource)) ticket[0].save_changes(changeset.author, comment, date, db) self._notify(ticket[0], date) except Exception, e: self.log.error( "Unexpected error while processing ticket " "#%s: %s", tkt_id, exception_to_unicode(e))
def setUp(self): self.env = EnvironmentStub() self.env.path = tempfile.mkdtemp(prefix='trac-tempenv-') self.attachments_dir = os.path.join(self.env.path, 'files', 'attachments') self.env.enable_component(TicketOnlyViewsTicket) self.env.config.set('trac', 'permission_policies', 'TicketOnlyViewsTicket, LegacyAttachmentPolicy') self.env.config.set('attachment', 'max_size', 512) self.perm = PermissionCache(self.env) self.datetime = datetime(2001, 1, 1, 1, 1, 1, 0, utc) with self.env.db_transaction as db: db("INSERT INTO wiki (name,version) VALUES ('WikiStart',1)") db("INSERT INTO wiki (name,version) VALUES ('SomePage',1)") db("INSERT INTO ticket (id) VALUES (42)") db("INSERT INTO ticket (id) VALUES (43)") db("INSERT INTO attachment VALUES (%s,%s,%s,%s,%s,%s,%s,%s)", ('ticket', '43', 'foo.txt', 8, to_utimestamp(self.datetime), 'A comment', 'joe', '::1'))
def get_allowed_owners(self, ticket=None): """Returns a list of permitted ticket owners (those possessing the TICKET_MODIFY permission). Returns `None` if the option `[ticket]` `restrict_owner` is `False`. If `ticket` is not `None`, fine-grained permission checks are used to determine the allowed owners for the specified resource. :since: 1.0.3 """ if self.restrict_owner: allowed_owners = [] for user in PermissionSystem(self.env) \ .get_users_with_permission('TICKET_MODIFY'): if not ticket or \ 'TICKET_MODIFY' in PermissionCache(self.env, user, ticket.resource): allowed_owners.append(user) allowed_owners.sort() return allowed_owners
def test_bitten_keeps_order_of_revisions_from_versioncontrol(self): # Trac's API specifies that they are sorted chronological (backwards) # We must not assume that these revision numbers can be sorted later on, # for example the mercurial plugin will return the revisions as strings # (e.g. '880:4c19fa95fb9e') config = BuildConfig(self.env, name='test', path='trunk') config.insert() platform = TargetPlatform(self.env, config='test', name='any') platform.insert() PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW') req = Mock(method='GET', base_path='', cgi_location='', path_info='/build/' + config.name, href=Href('/trac'), args={}, chrome={}, authname='joe', perm=PermissionCache(self.env, 'joe')) # revisions are intentionally not sorted in any way - bitten should just keep them! revision_ids = [5, 8, 2] revision_list = [('trunk', revision, 'edit') for revision in revision_ids] root = Mock(get_entries=lambda: ['foo'], get_history=lambda: revision_list) self.repos = Mock(get_node=lambda path, rev=None: root, sync=lambda: None, normalize_path=lambda path: path, normalize_rev=lambda rev: rev, youngest_rev=5) self.repos.authz = Mock(has_permission=lambda path: True, assert_permission=lambda path: None) module = BuildConfigController(self.env) assert module.match_request(req) _, data, _ = module.process_request(req) actual_revision_ids = data['config']['revisions'] self.assertEquals(revision_ids, actual_revision_ids)
def test_view_config(self): config = BuildConfig(self.env, name='test', path='trunk') config.insert() platform = TargetPlatform(self.env, config='test', name='any') platform.insert() PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW') req = Mock(method='GET', base_path='', cgi_location='', path_info='/build/test', href=Href('/trac'), args={}, chrome={}, authname='joe', perm=PermissionCache(self.env, 'joe')) root = Mock(get_entries=lambda: ['foo'], get_history=lambda: [('trunk', rev, 'edit') for rev in range(123, 111, -1)]) self.repos = Mock(get_node=lambda path, rev=None: root, sync=lambda: None, normalize_path=lambda path: path, normalize_rev=lambda rev: rev, youngest_rev=123) self.repos.authz = Mock(has_permission=lambda path: True, assert_permission=lambda path: None) module = BuildConfigController(self.env) assert module.match_request(req) _, data, _ = module.process_request(req) self.assertEqual('view_config', data['page_mode']) assert not 'next' in req.chrome['links'] from trac.resource import Resource self.assertEquals(Resource('build', 'test'), data['context'].resource) self.assertEquals([], data['config']['attachments']['attachments']) self.assertEquals('/trac/attachment/build/test/', data['config']['attachments']['attach_href'])
def test_process_update_platform_cancel(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() platform = TargetPlatform(self.env, config='foo', name='any') platform.insert() redirected_to = [] def redirect(url): redirected_to.append(url) raise RequestDone req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), abs_href=Href('http://example.org/'), redirect=redirect, authname='joe', args={ 'cancel': '', 'edit': '', 'name': 'Changed', 'property_0': 'family', 'pattern_0': 'posix' }) provider = BuildConfigurationsAdminPageProvider(self.env) try: provider.render_admin_panel(req, 'bitten', 'configs', 'foo/%d' % platform.id) self.fail('Expected RequestDone') except RequestDone: self.assertEqual('http://example.org/admin/bitten/configs/foo', redirected_to[0]) platforms = list(TargetPlatform.select(self.env, config='foo')) self.assertEqual(1, len(platforms)) self.assertEqual('any', platforms[0].name) self.assertEqual([], platforms[0].rules)
def filter_subscriptions(self, event, subscriptions): action = '%s_VIEW' % event.realm.upper() for subscription in subscriptions: if event.realm in self.exception_realms: yield subscription continue sid, auth = subscription[1:3] # PermissionCache already takes care of sid = None if not auth: sid = 'anonymous' perm = PermissionCache(self.env, sid) resource_id = get_target_id(event.target) self.log.debug("Checking *_VIEW permission on event for resource " "%s:%s", event.realm, resource_id) if action in perm and action in perm(event.realm, resource_id): yield subscription else: self.log.debug("Filtering %s because of %s rule", sid, self.__class__.__name__)
def test_raise_404(self): PermissionSystem(self.env).grant_permission('joe', 'BUILD_VIEW') module = BuildConfigController(self.env) req = Mock(method='GET', base_path='', cgi_location='', path_info='/build/nonexisting', href=Href('/trac'), args={}, chrome={}, authname='joe', perm=PermissionCache(self.env, 'joe')) self.failUnless(module.match_request(req)) try: module.process_request(req) except Exception, e: self.failUnless(isinstance(e, HTTPNotFound)) self.assertEquals( str(e), "404 Not Found (Build configuration " "'nonexisting' does not exist.)") return
def test_process_add_config_invalid_name(self): req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), chrome={'warnings': []}, href=Href('/'), authname='joe', args={ 'add': '', 'name': 'no spaces allowed' }) provider = BuildConfigurationsAdminPageProvider(self.env) try: provider.render_admin_panel(req, 'bitten', 'configs', '') self.fail('Expected TracError') except TracError, e: self.assertEqual( 'The field "name" may only contain letters, ' 'digits, periods, or dashes.', e.message) self.assertEqual('Add Configuration', e.title)
def test_process_remove_config_bad_selection(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), args={ 'remove': '', 'sel': 'baz' }) provider = BuildConfigurationsAdminPageProvider(self.env) try: provider.render_admin_panel(req, 'bitten', 'configs', '') self.fail('Expected TracError') except TracError, e: self.assertEqual("Configuration 'baz' not found", e.message)
def _update_tickets(self, tickets, changeset, comment, date): """Update the tickets with the given comment.""" authname = self._authname(changeset) perm = PermissionCache(self.env, authname) for tkt_id, cmds in tickets.iteritems(): try: self.log.debug("Updating ticket #%d", tkt_id) save = False with self.env.db_transaction: ticket = Ticket(self.env, tkt_id) ticket_perm = perm(ticket.resource) for cmd in cmds: if cmd(ticket, changeset, ticket_perm) is not False: save = True if save: ticket.save_changes(authname, comment, date) if save: self._notify(ticket, date, changeset.author, comment) except Exception as e: self.log.error("Unexpected error while processing ticket " "#%s: %s", tkt_id, exception_to_unicode(e))
def test_new_config_submit_with_invalid_path(self): req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), authname='joe', args={ 'add': '', 'name': 'foo', 'path': 'invalid/path' }) def get_node(path, rev=None): raise TracError('No such node') self.repos.get_node = get_node provider = BuildConfigurationsAdminPageProvider(self.env) try: provider.render_admin_panel(req, 'bitten', 'configs', '') self.fail('Expected TracError') except TracError, e: self.failUnless('Invalid Repository Path' in e.message)
def process_request(self, req): parent_dir = os.path.dirname(self.env.path) #env_paths = dict([(filename, os.path.join(parent_dir, filename)) # for filename in os.listdir(parent_dir)]) projects = [] for env_name in os.listdir(parent_dir): env_path = os.path.join(parent_dir, env_name) # Don't list this environment if env_path == self.env.path: continue try: env = open_environment(env_path) try: #self.log.debug(env.path) env_perm = PermissionCache(env, req.authname) #self.log.debug(env_perm.perms) if env_perm.has_permission('PROJECT_VIEW'): projects.append({ 'name': env.project_name, 'description': env.project_description, 'href': req.href.projects(env_name), }) except Exception, e: # Only show errors to admins to prevent excessive disclosure if req.perm.has_permission('TRACFORGE_ADMIN'): projects.append({ 'name': env.project_name, 'description': to_unicode(e) }) except Exception, e: if req.perm.has_permission('TRACFORGE_ADMIN'): projects.append({ 'name': env_path, 'description': to_unicode(e), })
def setUp(self): self.env = EnvironmentStub(default_data=True, enable=['trac.*', 'tractags.*']) self.env.path = tempfile.mkdtemp() setup = TagSetup(self.env) # Current tractags schema is partially setup with enabled component. # Revert these changes for getting a clean setup. self._revert_tractags_schema_init() setup.upgrade_environment() self.perms = PermissionSystem(self.env) self.tag_s = TagSystem(self.env) # Populate table with initial test data. self.env.db_transaction(""" INSERT INTO tags (tagspace, name, tag) VALUES ('wiki', 'WikiStart', 'tag1') """) self.req = Mock(authname='editor') # Mock an anonymous request. self.req.perm = PermissionCache(self.env)
def test_process_build_step_no_post(self): BuildConfig(self.env, 'test', path='somepath', active=True, recipe='<build></build>').insert() build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, started=42) build.insert() outheaders = {} outbody = StringIO() req = Mock(method='GET', base_path='', path_info='/builds/%d/steps/' % build.id, href=Href('/trac'), remote_addr='127.0.0.1', args={}, perm=PermissionCache(self.env, 'hal'), send_response=lambda x: outheaders.setdefault('Status', x), send_header=lambda x, y: outheaders.setdefault(x, y), write=outbody.write, incookie=Cookie('trac_auth=')) module = BuildMaster(self.env) module._start_new_step(build, 'foo').insert() assert module.match_request(req) self.assertRaises(RequestDone, module.process_request, req) self.assertEqual(405, outheaders['Status']) self.assertEqual('Method GET not allowed', outbody.getvalue())
def test_initiate_build_no_such_build(self): outheaders = {} outbody = StringIO() req = Mock(method='GET', base_path='', path_info='/builds/123', href=Href('/trac'), remote_addr='127.0.0.1', args={}, perm=PermissionCache(self.env, 'hal'), send_response=lambda x: outheaders.setdefault('Status', x), send_header=lambda x, y: outheaders.setdefault(x, y), write=outbody.write, incookie=Cookie('trac_auth=')) module = BuildMaster(self.env) assert module.match_request(req) self.assertRaises(RequestDone, module.process_request, req) self.assertEquals(404, outheaders['Status']) self.assertEquals('No such build (123)', outbody.getvalue())
def test_process_remove_config_cancel(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() BuildConfig(self.env, name='bar', label='Bar', path='branches/bar', min_rev='123', max_rev='456').insert() redirected_to = [] def redirect(url): redirected_to.append(url) raise RequestDone req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), abs_href=Href('http://example.org/'), redirect=redirect, args={ 'cancel': '', 'sel': 'bar' }) provider = BuildConfigurationsAdminPageProvider(self.env) try: provider.render_admin_panel(req, 'bitten', 'configs', '') self.fail('Expected RequestDone') except RequestDone: self.assertEqual('http://example.org/admin/bitten/configs', redirected_to[0]) configs = list(BuildConfig.select(self.env, include_inactive=True)) self.assertEqual(2, len(configs))
def test_process_update_config_invalid_name(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), args={ 'save': '', 'name': 'no spaces allowed' }, authname='joe', chrome={'warnings': []}, href=Href('/')) provider = BuildConfigurationsAdminPageProvider(self.env) provider.render_admin_panel(req, 'bitten', 'configs', 'foo') self.failUnless(req.chrome['warnings'], "No warnings?") self.assertEquals(req.chrome['warnings'], ['The field "name" may ' \ 'only contain letters, digits, periods, or dashes.'])
def test_process_update_config_no_name(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), args={ 'save': '', 'name': '' }, authname='joe', chrome={'warnings': []}, href=Href('/')) provider = BuildConfigurationsAdminPageProvider(self.env) provider.render_admin_panel(req, 'bitten', 'configs', 'foo') self.failUnless(req.chrome['warnings'], "No warnings?") self.assertEquals(['Missing required field "name".'], req.chrome['warnings'])
def process_request(self, req): raise TracError('How did I get here?') path_info = req.path_info[10:] if path_info: project = path_info.split('/', 1)[0] # Check that we aren't trying to recurse (possible link loop) if project == os.path.basename(self.env.path): req.redirect(req.href()) # Assert permissions on the desination environment project_path = os.path.join(os.path.dirname(self.env.path), project) try: project_env = open_environment(project_path) except IOError: raise TracError('No such project "%s" at %s'% (project,project_path)) project_perm = PermissionCache(project_env, req.authname) project_perm.assert_permission('PROJECT_VIEW') return self._send_project(req, path_info) else: return self._send_index(req)
def test_process_update_config_invalid_path(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), authname='joe', chrome={'warnings': []}, href=Href('/'), args={ 'save': '', 'name': 'foo', 'path': 'invalid/path' }) def get_node(path, rev=None): raise TracError('No such node') self.repos.get_node = get_node provider = BuildConfigurationsAdminPageProvider(self.env) provider.render_admin_panel(req, 'bitten', 'configs', 'foo') self.failUnless(req.chrome['warnings'], "No warnings?") self.assertTrue( req.chrome['warnings'] in ( # single repos / 0.11 [ 'Invalid Repository Path: "invalid/path" does not exist ' 'within the "(default)" repository.' ], # multi-repos / 0.12+ ['Invalid Repository Path "invalid/path".']), req.chrome['warnings'])
def test_process_update_config_non_wellformed_recipe(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), authname='joe', chrome={'warnings': []}, href=Href('/'), args={ 'save': '', 'name': 'foo', 'recipe': 'not_xml' }) provider = BuildConfigurationsAdminPageProvider(self.env) provider.render_admin_panel(req, 'bitten', 'configs', 'foo') self.failUnless(req.chrome['warnings'], "No warnings?") self.assertEquals(['Failure parsing recipe: syntax error: line 1, ' \ 'column 0.'], req.chrome['warnings'])
def test_create_related_tickets(self): """ Test the list of possible related ticket to create given a type and a login permissions. """ req = self.teh.mock_request(name_product_owner) story = self.teh.create_ticket(Type.USER_STORY, props={ Key.OWNER: name_product_owner, Key.STORY_PRIORITY: 'Mandatory' }) # Build a fake data dictionary containing the ticket data = {Key.TICKET: story} AgiloTicketModule(self.env)._prepare_create_referenced(req, data) # Now check that being a Product Owner there is no link to create a task self.assert_false(Type.TASK in data['create_referenced']) # Now login as a team member and the link should be there req.perm = PermissionCache(self.env, name_team_member) req.authname = name_team_member AgiloTicketModule(self.env)._prepare_create_referenced(req, data) allowed_links = data['create_referenced'] allowed_destination_types = [l.dest_type for l in allowed_links] self.assert_true(Type.TASK in allowed_destination_types)
def _get_perm(self, req): if isinstance(req.session, FakeSession): return FakePerm() else: return PermissionCache(self.env, req.authname)
def _dispatch(self, req, env): req.perm = PermissionCache(env, req.authname) return RequestDispatcher(env).dispatch(req)
def MockRequest(env, **kwargs): """Request object for testing. Keyword arguments populate an `environ` dictionary and the callbacks. If `authname` is specified in a keyword arguments a `PermissionCache` object is created, otherwise if `authname` is not specified or is `None` a `MockPerm` object is used and the `authname` is set to 'anonymous'. The following keyword arguments are commonly used: :keyword args: dictionary of request arguments :keyword authname: the name of the authenticated user, or 'anonymous' :keyword method: the HTTP request method :keyword path_info: the request path inside the application Additionally `cookie`, `format`, `language`, `lc_time`, `locale`, `remote_addr`, `remote_user`, `script_name`, `server_name`, `server_port` and `tz` can be specified as keyword arguments. :since: 1.0.11 """ authname = kwargs.get('authname') if authname is None: authname = 'anonymous' perm = MockPerm() else: perm = PermissionCache(env, authname) def convert(val): if isinstance(val, bool): return unicode(int(val)) elif isinstance(val, numbers.Real): return unicode(val) elif isinstance(val, (list, tuple)): return [convert(v) for v in val] else: return val if 'arg_list' in kwargs: arg_list = [(k, convert(v)) for k, v in kwargs['arg_list']] args = arg_list_to_args(arg_list) else: args = _RequestArgs() args.update( (k, convert(v)) for k, v in kwargs.get('args', {}).iteritems()) arg_list = [(name, value) for name in args for value in args.getlist(name)] environ = { 'trac.base_url': env.abs_href(), 'wsgi.url_scheme': 'http', 'HTTP_ACCEPT_LANGUAGE': kwargs.get('language', ''), 'HTTP_COOKIE': kwargs.get('cookie', ''), 'PATH_INFO': kwargs.get('path_info', '/'), 'REQUEST_METHOD': kwargs.get('method', 'GET'), 'REMOTE_ADDR': kwargs.get('remote_addr', '127.0.0.1'), 'REMOTE_USER': kwargs.get('remote_user', authname), 'SCRIPT_NAME': kwargs.get('script_name', '/trac.cgi'), 'SERVER_NAME': kwargs.get('server_name', 'localhost'), 'SERVER_PORT': kwargs.get('server_port', '80'), } for key in environ: if isinstance(environ[key], unicode): environ[key] = environ[key].encode('utf-8') status_sent = [] headers_sent = {} response_sent = io.BytesIO() def start_response(status, headers, exc_info=None): status_sent.append(status) headers_sent.update(dict(headers)) return response_sent.write req = Mock(Request, environ, start_response) req.status_sent = status_sent req.headers_sent = headers_sent req.response_sent = response_sent req.callbacks.update({ 'arg_list': lambda req: arg_list, 'args': lambda req: args, 'authname': lambda req: authname, 'chrome': Chrome(env).prepare_request, 'form_token': lambda req: kwargs.get('form_token', 0), 'lc_time': lambda req: kwargs.get('lc_time', locale_en), 'locale': lambda req: kwargs.get('locale'), 'perm': lambda req: perm, 'session': lambda req: Session(env, req), 'tz': lambda req: kwargs.get('tz', utc), 'use_xsendfile': lambda req: False, 'xsendfile_header': lambda req: None, 'configurable_headers': lambda req: [], }) return req
def __init__(self, env, authname='anonymous'): self.perm = PermissionCache(env, authname)