def setUp(self): self.env = EnvironmentStub(default_data=True, enable=[ default_workflow.ConfigurableTicketWorkflow, DefaultPermissionPolicy, DefaultPermissionStore, BatchModifyModule, api.TicketSystem, web_ui.TicketModule ]) self.env.config.set('trac', 'permission_policies', 'DefaultPermissionPolicy') self.env.config.set('ticket-custom', 'text1', 'text') self.env.config.set('ticket-custom', 'text1.max_size', 5) self.env.config.set('ticket-custom', 'time1', 'time') self.env.config.set('ticket-custom', 'time1.format', 'date') self.env.config.set('ticket-workflow', 'acknowledge', '* -> acknowledged') ps = PermissionSystem(self.env) ps.grant_permission('has_ta_&_bm', 'TICKET_ADMIN') ps.grant_permission('has_bm', 'TICKET_BATCH_MODIFY') ps.grant_permission('has_ta_&_bm', 'TICKET_BATCH_MODIFY') session = DetachedSession(self.env, 'has_ta_&_bm') session.set('query_href', '') session.save() session = DetachedSession(self.env, 'has_bm') session.set('query_href', '') session.save() self._insert_ticket('Ticket 1', reporter='user1', component='component1', description='the desc', keywords='foo one', status='new') self._insert_ticket('Ticket 2', reporter='user1', component='component2', description='the desc', keywords='baz two', status='new')
def test_find_session_by_attr(self, env, helper): ds = DetachedSession(env, 'foo') ds['bar'] = 'baz' ds.save() ds = DetachedSession(env, 'wrong') ds['bar'] = 'not baz' ds.save() assert helper.find_session_by_attr('bar', 'baz') == ['foo']
def create_session(self, authname_base, attributes): """Create a new authenticated session. (In trac, authenticated sessions are, essentially “user accounts”, so this creates a new account or “login” on the trac.) If possible, the session is created with an ``sid`` of ``authname_base``. If a session already exists with that ``sid``, then a suffix is added to make the ``sid`` unique. The attributes of the new session are initialized from the ``attributes`` argument, if any. The ``sid`` of the new session is returned. """ if not attributes: raise ValueError("Attributes required for new session") for suffix in self.uniquifier_suffixes(): authname = authname_base + suffix if self.permission_exists_for(authname): continue ds = DetachedSession(self.env, authname) # At least in 0.12.2, this means no session exists. is_new = ds.last_visit == 0 and len(ds) == 0 if is_new: break for key, value in attributes.items(): ds[key] = value or '' ds.save() return authname
def _add_session(self, sid, values=None, **attrs): session = DetachedSession(self.env, sid) if values is not None: attrs.update(values) for name, value in attrs.iteritems(): session[name] = value session.save()
def getSession(self, queueid): cursor = self.env.get_db_cnx().cursor() cursor.execute("SELECT status, owner FROM buildqueue WHERE id = %s", (queueid,)) if cursor.rowcount != 1: return False row = cursor.fetchone() if row[0] != 90: return False if find(row[1], '@') != -1: return DetachedSession(self.env, "qatbot") return DetachedSession(self.env, row[1])
def _create_request(self, authname='anonymous', **kwargs): kw = { 'perm': PermissionCache(self.env, authname), 'args': {}, 'callbacks': {}, 'path_info': '', 'form_token': None, 'href': self.env.href, 'abs_href': self.env.abs_href, 'tz': utc, 'locale': None, 'lc_time': None, 'session': DetachedSession(self.env, authname), 'authname': authname, 'chrome': { 'notices': [], 'warnings': [] }, 'method': None, 'get_header': lambda v: None, 'is_xhr': False } kw.update(kwargs) def send(self, content, content_type='text/html', status=200): raise RequestDone return Mock(send=send, **kw)
def _create_request(self, authname='anonymous', **kwargs): kw = { 'path_info': '/', 'perm': MockPerm(), 'args': _RequestArgs(), 'href': self.env.href, 'abs_href': self.env.abs_href, 'tz': utc, 'locale': None, 'lc_time': locale_en, 'session': DetachedSession(self.env, authname), 'authname': authname, 'chrome': { 'notices': [], 'warnings': [] }, 'method': None, 'get_header': lambda v: None, 'is_xhr': False, 'form_token': None } if 'args' in kwargs: kw['args'].update(kwargs.pop('args')) kw.update(kwargs) def redirect(url, permanent=False): raise RequestDone return Mock(add_redirect_listener=lambda x: [].append(x), redirect=redirect, **kw)
def test_session_set(self): """Verify that setting a variable in a session to the default value removes it from the session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')" ) session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) # Setting the variable to the default value removes the variable with self.env.db_transaction as db: session.set('foo', 'default', 'default') session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) # Setting the variable to a value different from the default sets it with self.env.db_transaction as db: session.set('foo', 'something', 'default') session.save() self.assertEqual( 'something', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0])
def setUp(self): self.env = EnvironmentStub(default_data=True, enable=[ default_workflow.ConfigurableTicketWorkflow, DefaultPermissionPolicy, DefaultPermissionStore, BatchModifyModule, api.TicketSystem, web_ui.TicketModule ]) self.env.config.set('trac', 'permission_policies', 'DefaultPermissionPolicy') ps = PermissionSystem(self.env) ps.grant_permission('has_ta_&_bm', 'TICKET_ADMIN') ps.grant_permission('has_bm', 'TICKET_BATCH_MODIFY') ps.grant_permission('has_ta_&_bm', 'TICKET_BATCH_MODIFY') session = DetachedSession(self.env, 'has_ta_&_bm') session.set('query_href', '') session.save() session = DetachedSession(self.env, 'has_bm') session.set('query_href', '') session.save()
def test_session_set_email(self): """Setting session email invalidates known_users cache.""" session = DetachedSession(self.env, 'user') session['email'] = '*****@*****.**' self.assertEqual([], list(self.env.get_known_users())) session.save() email = None for sid, name, email in self.env.get_known_users(): if sid == 'user': break self.assertEqual(session['email'], email)
def test_session_set_name(self): """Setting session name invalidates known_users cache.""" session = DetachedSession(self.env, 'user') session['name'] = 'The User' self.assertEqual([], list(self.env.get_known_users())) session.save() name = None for sid, name, email in self.env.get_known_users(): if sid == 'user': break self.assertEqual(session['name'], name)
def test_create_new_session(self): """Setting attribute on a new session invalidates known_users cache.""" sid = 'user' session = DetachedSession(self.env, sid) session.authenticated = True self.assertEqual([], list(self.env.get_known_users())) session.save() known_users = list(self.env.get_known_users()) self.assertEqual(1, len(known_users)) self.assertEqual(sid, known_users[0][0]) self.assertIsNone(known_users[0][1]) self.assertIsNone(known_users[0][2])
def test_find_session_by_identity_url(self, env, userdb): bare_id_token = {'iss': 'https://example.net', 'sub': '42'} id_token = { 'iss': 'https://example.net', 'sub': '42', 'openid_id': 'https://example.org/foo' } ds = DetachedSession(env, 'bar') ds['openid_session_identity_url_data'] = 'https://example.org/foo' ds.save() assert userdb.find_session(bare_id_token) is None assert userdb.find_session(id_token) == 'bar' assert userdb.find_session(bare_id_token) == 'bar'
def test_session_set_email(self): """Setting session email invalidates known_users cache.""" sid = 'user' email = '*****@*****.**' session = DetachedSession(self.env, sid) session['email'] = email self.assertEqual([], list(self.env.get_known_users())) session.save() known_users = list(self.env.get_known_users()) self.assertEqual(1, len(known_users)) self.assertEqual(sid, known_users[0][0]) self.assertIsNone(known_users[0][1]) self.assertEqual(email, known_users[0][2])
def test_session_set_name(self): """Setting session name invalidates known_users cache.""" sid = 'user' name = 'The User' session = DetachedSession(self.env, sid) session['name'] = name self.assertEqual([], list(self.env.get_known_users())) session.save() known_users = list(self.env.get_known_users()) self.assertEqual(1, len(known_users)) self.assertEqual(sid, known_users[0][0]) self.assertEqual(name, known_users[0][1]) self.assertIsNone(known_users[0][2])
def test_create_new_anonymous_session(self): """Setting attribute on a new anonymous session doesn't invalidate known_users cache.""" sid = 'anonymous' session = DetachedSession(self.env, sid) session.authenticated = False self.assertEqual([], list(self.env.get_known_users())) # insert a session record without invalidating cache self.env.insert_users([('user', 'Name' '*****@*****.**', 1)]) session.save() self.assertEqual([], list(self.env.get_known_users())) self.env.invalidate_known_users_cache() self.assertEqual(1, len(list(self.env.get_known_users())))
def test_delete_detached_session_var(self): """ Verify that removing a variable in a session not associated with a request deletes the variable from the database. """ cursor = self.db.cursor() cursor.execute("INSERT INTO session VALUES ('john', 1, 0)") cursor.execute("INSERT INTO session_attribute VALUES " "('john', 1, 'foo', 'bar')") session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) del session['foo'] session.save() cursor.execute("SELECT COUNT(*) FROM session_attribute " "WHERE sid='john' AND name='foo'") self.assertEqual(0, cursor.fetchone()[0])
def test_modify_detached_session(self): """ Verify that modifying a variable in a session not associated with a request updates the database accordingly. """ cursor = self.db.cursor() cursor.execute("INSERT INTO session VALUES ('john', 1, 0)") cursor.execute("INSERT INTO session_attribute VALUES " "('john', 1, 'foo', 'bar')") session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() cursor.execute("SELECT value FROM session_attribute " "WHERE sid='john' AND name='foo'") self.assertEqual('baz', cursor.fetchone()[0])
def test_modify_detached_session(self): """ Verify that modifying a variable in a session not associated with a request updates the database accordingly. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')") session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() self.assertEqual('baz', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0])
def test_delete_detached_session_var(self): """ Verify that removing a variable in a session not associated with a request deletes the variable from the database. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')") session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) del session['foo'] session.save() self.assertEqual(0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0])
def test_get_main_page(self): req = Mock(path_info='/tags', args={}, authname='reader', perm=self.reader, href=self.href, method='GET', chrome=dict(static_hash='hashme!'), session=DetachedSession(self.env, 'reader'), locale='', tz='') template, data, content_type = self.tag_rh.process_request(req) self.assertEquals('tag_view.html', template) self.assertEquals(None, content_type) self.assertEquals([ 'checked_realms', 'mincount', 'page_title', 'tag_body', 'tag_query', 'tag_realms' ], sorted(data.keys()))
def associate_session(self, authname, iss, sub): ds = DetachedSession(self.env, authname) ds[self.SUBJECT_SKEY] = self.subject_uri(iss, sub) ds.save()
def _do_process(self, req): """Handle the redirect from the OpenID server. """ db = self.env.get_db_cnx() oidconsumer, oidsession = self._get_consumer(req, db) # Ask the library to check the response that the server sent # us. Status is a code indicating the response type. info is # either None or a string containing more information about # the return type. info = oidconsumer.complete(req.args, req.args['openid.return_to']) css_class = 'error' if info.status == consumer.FAILURE and info.identity_url: # In the case of failure, if info is non-None, it is the # URL that we were verifying. We include it in the error # message to help the user figure out what happened. fmt = "Verification of %s failed: %s" message = fmt % (cgi.escape(info.identity_url), info.message) elif info.status == consumer.SUCCESS: # Success means that the transaction completed without # error. If info is None, it means that the user cancelled # the verification. css_class = 'alert' session_attr = {} # attributes for new "user" # This is a successful verification attempt. If this # was a real application, we would do our login, # comment posting, etc. here. fmt = "You have successfully verified %s as your identity." message = fmt % (cgi.escape(info.identity_url), ) remote_user = info.identity_url sreg_info = sreg.SRegResponse.fromSuccessResponse(info) or {} ax_response = ax.FetchResponse.fromSuccessResponse(info) ax_info = {} if ax_response: for alias, uri in self.openid_ax_attrs.items(): values = ax_response.data.get(uri, []) if values: ax_info[alias] = values[0] email = (ax_info.get('email') or ax_info.get('email2') or sreg_info.get('email')) fullname = (' '.join( filter(None, map(ax_info.get, ('firstname', 'lastname')))) or sreg_info.get('fullname') or (email and email.split('@', 1)[0].replace('.', ' ').title())) nickname = sreg_info.get('nickname') if self.groups_to_request and TeamsResponse: teams_response = TeamsResponse.fromSuccessResponse(info) if teams_response: # be careful not to make user a member of any trac groups # not named in groups_to_request teams = set(teams_response.teams).intersection( self.groups_to_request) if teams: session_attr['openid.teams'] = ','.join(teams) if self.strip_protocol: remote_user = remote_user[remote_user.find('://') + 3:] if self.strip_trailing_slash and remote_user[-1] == '/': remote_user = remote_user[:-1] if info.endpoint.canonicalID: # You should authorize i-name users by their canonicalID, # rather than their more human-friendly identifiers. That # way their account with you is not compromised if their # i-name registration expires and is bought by someone else. message += ( " This is an i-name, and its persistent ID is %s" % (cgi.escape(info.endpoint.canonicalID), )) remote_user = info.endpoint.canonicalID allowed = True if self.re_white_list: self.env.log.debug( "Filtering REMOTE_USER '%s' through white-list." % remote_user) allowed = False for item in self.re_white_list: if not allowed and item.match(remote_user): allowed = True self.env.log.debug("User white-listed.") if allowed and self.re_black_list: self.env.log.debug( "Filtering REMOTE_USER '%s' through black-list." % remote_user) for item in self.re_black_list: if item.match(remote_user): allowed = False self.env.log.debug("User black-listed.") if allowed and self.re_email_white_list: self.env.log.debug( "Filtering email %r through email white-list." % email) allowed = False if email: for item in self.re_email_white_list: if not allowed and item.match(email): allowed = True self.env.log.debug("User email white-listed.") if allowed and self.check_list: allowed = False params = {self.check_list_key: remote_user} if email: params['email'] = email url = self.check_list + '?' + urllib.urlencode(params) self.env.log.debug('OpenID check list URL: %s' % url) try: result = json.load(urllib.urlopen(url)) if result[self.check_list_key]: if self.check_list_username: cl_username = unicode( result[self.check_list_username]) if not cl_username: raise ValueError("Bad value for username") allowed = True except Exception, ex: self.env.log.error('OpenID check_list failed: %s' % ex) if allowed: cookie = hex_entropy() cookie_lifetime = self.trac_auth_cookie_lifetime req.outcookie['trac_auth'] = cookie req.outcookie['trac_auth']['path'] = req.href() if cookie_lifetime > 0: req.outcookie['trac_auth']['expires'] = cookie_lifetime session_attr[ self.openid_session_identity_url_key] = info.identity_url if email: session_attr['email'] = email if fullname: session_attr['name'] = fullname self._commit_oidsession(oidsession, req) if self.check_list and self.check_list_username: authname = cl_username elif self.use_nickname_as_authname and nickname: authname = nickname elif session_attr.get('name'): authname = session_attr['name'] if self.combined_username: authname = '%s <%s>' % (authname, remote_user) else: authname = remote_user # Possibly lower-case the authname. if self.lowercase_authname: authname = authname.lower() if self.trust_authname: ds = DetachedSession(self.env, authname) else: # Make authname unique in case of collisions # # XXX: We ought to first look for an existing authenticated # session with matching identity_url, and just use that # for the authid. (E.g. what if the user changes his # fullname at the openid provider?) However, trac does # not seem to provide an API for searching sessions other # than by sid/authname. # def authnames(base): yield base for attempt in itertools.count(2): yield "%s (%d)" % (base, attempt) existing_users_and_groups = set( user for user, perm in PermissionSystem( self.env).get_all_permissions()) for authname in authnames(authname): ds = DetachedSession(self.env, authname) if ds.last_visit == 0 and len(ds) == 0: # At least in 0.12.2, this mean no session exists. if authname in existing_users_and_groups: # Permissions are already defined for this user continue break ds_identity = ds.get( self.openid_session_identity_url_key) if ds_identity == info.identity_url: # No collision break if ds and (ds.last_visit != 0 or len(ds) > 0): # The user already exists, update team membership # XXX: Should also update name and/or email? (This would # be an API change.) for name in ['openid.teams']: if name in session_attr: ds[name] = session_attr[name] elif name in ds: del ds[name] ds.save() else: # We are creating a new "user". Set attributes on the # current anonymous session. It will be promoted to # the new authenticated session on the next request # (by Session.__init__). # # NB: avoid dict.update here to ensure that # DetachedSession.__getitem__ gets a chance to # normalize values for name, value in session_attr.items(): req.session[name] = value req.authname = authname db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute( "INSERT INTO auth_cookie (cookie,name,ipnr,time) " "VALUES (%s, %s, %s, %s)", (cookie, authname, self._get_masked_address( req.remote_addr), int(time.time()))) db.commit() req.redirect( req.session.get('oid.referer') or self.env.abs_href()) else: message = 'You are not allowed here.'
def get_permission_groups(self, username): ds = DetachedSession(self.env, username) return ds.get('openid.teams', '').split(',')
def _add_session(self, sid, **attrs): session = DetachedSession(self.env, sid) for name, value in attrs.iteritems(): session[name] = value session.save()
def test_find_session(self, env, userdb): ds = DetachedSession(env, 'foo') ds['trac_oidc.subject'] = 'https://example.net?sub=42' ds.save() sid = userdb.find_session({'iss': 'https://example.net', 'sub': '42'}) assert sid == 'foo'
def test_associate_session(self, env, userdb): userdb.associate_session('foo', 'https://example.net', '42') ds = DetachedSession(env, 'foo') assert ds[userdb.SUBJECT_SKEY] == 'https://example.net?sub=42'
def make_session(sid, last_visit): monkeypatch.setattr("time.time", lambda: last_visit) ds = DetachedSession(env, sid) ds['bar'] = 'baz' ds.save()
def test_create_session(self, env, helper): sid = helper.create_session('foo', {'name': 'Joe'}) assert sid == 'foo' ds = DetachedSession(env, 'foo') assert ds['name'] == 'Joe'