def _users_query(self, q, limit=10):
     from simplifiedpermissionsadminplugin.simplifiedpermissions import SimplifiedPermissions
     if SimplifiedPermissions and self.env.is_enabled(SimplifiedPermissions):
         sp = SimplifiedPermissions(self.env)
         # Keep track of users that have already been found to prevent
         # yielding duplicates of users belonging to several groups
         yielded_sids = set()
         for group, data in sp.group_memberships().items():
             for member in data['members']:
                 if q in member.sid and member.sid not in yielded_sids:
                     # if the 'never logged in' text changes, then update
                     # plugins/open/autocompleteplugin/autocompleteplugin/htdocs/js/jquery.tracautocomplete.js
                     yield {'sid': member.sid,
                            'name': member.get('name', member.sid),
                            'email': member.get('email','')}
                     yielded_sids.add(member.sid)
     else:
         perm = PermissionSystem(self.env)
         users = [sid
                  for sid, permission in perm.get_all_permissions()
                  if sid not in set("anonymous", "authenticated", "admin")]
         for sid in sorted(set(users)):
             if q in sid:
                 session = DetachedSession(self.env, sid)
                 yield {'sid': sid,
                        'name': session.get('name',''),
                        'email': session.get('email','Never logged in')}
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
 def add_known_user(self, username, name, email):
     session = DetachedSession(self.env, username)
     if name is not None:
         session["name"] = name
     if email is not None:
         session["email"] = email
     session.save()
Exemplo n.º 4
0
 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'
Exemplo n.º 5
0
    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])
Exemplo n.º 6
0
    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)
Exemplo n.º 7
0
    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)
Exemplo n.º 8
0
    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])
Exemplo n.º 9
0
    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])
Exemplo n.º 10
0
    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())))
Exemplo n.º 11
0
 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']
Exemplo n.º 12
0
    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])
Exemplo n.º 13
0
    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)
Exemplo n.º 14
0
    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)
Exemplo n.º 15
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.
        """
        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])
Exemplo n.º 16
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.
        """
        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 _get_ticket_data(self, req, results):
        ats = AgileToolsSystem(self.env)
        loc = LogicaOrderController(self.env)
        closed_statuses = loc.type_and_statuses_for_closed_statusgroups()

        # TODO calculate which statuses are closed using the query system
        # when it is able to handle this
        tickets = []
        for result in results:
            if result['status'] not in closed_statuses[result['type']]:
                filtered_result = dict((k, v)
                                   for k, v in result.iteritems()
                                   if k in self.fields)

                if "remaininghours" in filtered_result:
                    try:
                        hours = float(filtered_result["remaininghours"])
                    except (ValueError, TypeError):
                        hours = 0
                    del filtered_result["remaininghours"]
                else:
                    hours = 0

                if "effort" in filtered_result:
                    try:
                        storypoints = float(filtered_result['effort'])
                    except (ValueError, TypeError):
                        storypoints = 0
                else:
                    storypoints = 0

                reporter = filtered_result["reporter"]
                session = DetachedSession(self.env, reporter)

                filtered_result.update({
                    'id': result['id'],
                    'position': ats.position(result['id']),
                    'hours': hours,
                    'effort': storypoints,
                    'reporter': session.get('name', reporter),
                    'changetime': to_utimestamp(filtered_result['changetime'])
                    })

                tickets.append(filtered_result)

        return tickets
Exemplo n.º 18
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])
Exemplo n.º 19
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])
Exemplo n.º 20
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])
Exemplo n.º 21
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 _get_address_info(self, authname):
     "TODO: check env.get_known_users"
     # First check if it's a #define user
     sess = DetachedSession(self.env, authname)
     address = sess.get('email')
     real_name = None
     if not address:
         # Otherwise check if it's a valid email address
         real_name, address = self.parse_address(authname)
         if not address:
             if not sess.get('email'):
                 raise ValueError(_('User %(user)s has no email address set',
                                    user=authname))
             else:
                 raise ValueError(_('%(address)s is not a valid email address',
                                    address=address))
     if not real_name:
         real_name = sess.get('name')
     return real_name, address
Exemplo n.º 23
0
 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']
Exemplo n.º 24
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()))
Exemplo n.º 25
0
 def get_permission_groups(self, username):
     ds = DetachedSession(self.env, username)
     return ds.get('openid.teams', '').split(',')
Exemplo n.º 26
0
    def _do_process(self, req):
        """Handle the redirect from the OpenID server.
        """
        db = self.env.get_db_cnx()
        oidconsumer, session = 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"

            # 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

            reg_info = None

            ax_response = ax.FetchResponse.fromSuccessResponse(info)
            if ax_response:
                ax_data = ax_response.getExtensionArgs()
                email = ax_data.get("value.ext0.1", "")
                if email:
                    reg_info = {"email": email, "fullname": email.split("@", 1)[0].replace(".", " ").title()}

            if not reg_info:
                response = sreg.SRegResponse.fromSuccessResponse(info)
                if response:
                    reg_info = response.getExtensionArgs()

            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.check_list:
                params = {self.check_list_key: remote_user}
                if reg_info and reg_info.has_key("email") and len(reg_info["email"]) > 0:
                    params["email"] = reg_info["email"]
                url = self.check_list + "?" + urllib.urlencode(params)
                self.env.log.debug("OpenID check list URL: %s" % url)
                result = simplejson.load(urllib.urlopen(url))
                if not result[self.check_list_key]:
                    allowed = False
                elif self.check_list_username:
                    new_user = result[self.check_list_username]
                    if new_user:
                        remote_user = new_user

            if allowed:
                cookie = hex_entropy()

                req.outcookie["trac_auth"] = cookie
                req.outcookie["trac_auth"]["path"] = req.href()
                req.outcookie["trac_auth"]["expires"] = self.trac_auth_expires

                req.session[self.openid_session_identity_url_key] = info.identity_url

                if reg_info and reg_info.has_key("fullname") and len(reg_info["fullname"]) > 0:
                    req.session["name"] = reg_info["fullname"]
                if reg_info and reg_info.has_key("email") and len(reg_info["email"]) > 0:
                    req.session["email"] = reg_info["email"]

                self._commit_session(session, req)

                if self.combined_username and req.session["name"]:
                    remote_user = "******" % (req.session["name"], remote_user)
                else:
                    if req.session.has_key("name"):
                        remote_user = req.session["name"]

                    # Check if we generated a colliding remote_user and make the user unique
                    collisions = 0
                    cremote_user = remote_user
                    while True:
                        ds = DetachedSession(self.env, remote_user)
                        if not ds.last_visit:
                            # New session
                            break
                        if not ds.has_key(self.openid_session_identity_url_key):
                            # Old session, without the identity url set
                            # Save the identity url then (bascially adopt the session)
                            ds[self.openid_session_identity_url_key] = info.identity_url
                            ds.save()
                            break
                        if ds[self.openid_session_identity_url_key] == info.identity_url:
                            # No collision
                            break
                        # We got us a collision
                        # Make the thing unique
                        collisions += 1
                        remote_user = "******" % (cremote_user, collisions + 1)

                req.authname = remote_user

                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, remote_user, 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."
        elif info.status == consumer.CANCEL:
            # cancelled
            message = "Verification cancelled"
        elif info.status == consumer.SETUP_NEEDED:
            if info.setup_url:
                message = "<a href=%s>Setup needed</a>" % (quoteattr(info.setup_url),)
            else:
                # This means auth didn't succeed, but you're welcome to try
                # non-immediate mode.
                message = "Setup needed"
        else:
            # Either we don't understand the code or there is no
            # openid_url included with the error. Give a generic
            # failure message. The library should supply debug
            # information in a log.
            message = "Verification failed."

        self._commit_session(session, req)

        add_stylesheet(req, "authopenid/css/openid.css")
        add_script(req, "authopenid/js/openid-jquery.js")
        return (
            "openidlogin.html",
            {
                "images": req.href.chrome("authopenid/images") + "/",
                "action": req.href.openidverify(),
                "message": message,
                "signup": self.signup_link,
                "whatis": self.whatis_link,
                "css_class": css_class,
                "custom_provider_name": self.custom_provider_name,
                "custom_provider_label": self.custom_provider_label,
                "custom_provider_url": self.custom_provider_url,
                "custom_provider_image": self.custom_provider_image,
            },
            None,
        )
Exemplo n.º 27
0
    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.
        current_url = req.abs_href(req.path_info)
        info = oidconsumer.complete(req.args,current_url)

        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)

                # First look for an existing authenticated session with
                # matching identity_url.
                self.env.log.debug('Checking URL: %s' % info.identity_url)
                authname_for_identity_url = self.get_user(info.identity_url)
                if authname_for_identity_url:
                    authname = authname_for_identity_url
                    ds = DetachedSession(self.env, authname)
                    # 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:
                    # New identity URL -> create new authname/user.
                    if self.check_list and self.check_list_username:
                        authname = cl_username
                    elif self.use_email_as_authname and email:
                        authname = email
                    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
                        def authnames(base):
                            yield base
                            for attempt in itertools.count(2):
                                yield "%s (%d)" % (base, attempt)

                        users_and_groups_with_permissions = set(
                            user
                            for user, perm
                            in PermissionSystem(self.env).get_all_permissions())

                        for authname in authnames(authname):
                            ds = DetachedSession(self.env, authname)
                            # At least in 0.12.2, this means no session exists.
                            no_session_exists = ds.last_visit == 0 and len(ds) == 0
                            no_permissions_defined = authname not in users_and_groups_with_permissions
                            if (no_session_exists and no_permissions_defined):
                                # name is free :-)
                                break

                    # Set attributes for new user 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
                    self.env.log.info("Created new user '%s' for "
                        "OpenID identifier %s", authname, info.identity_url)

                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.'
Exemplo n.º 28
0
 def get_permission_groups(self, username):
     ds = DetachedSession(self.env, username)
     return ds.get('openid.teams', '').split(',')
Exemplo n.º 29
0
 def make_session(sid, last_visit):
     monkeypatch.setattr("time.time", lambda: last_visit)
     ds = DetachedSession(env, sid)
     ds['bar'] = 'baz'
     ds.save()
Exemplo n.º 30
0
 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'
Exemplo n.º 31
0
    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.'
Exemplo n.º 32
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()
Exemplo n.º 33
0
 def associate_session(self, authname, iss, sub):
     ds = DetachedSession(self.env, authname)
     ds[self.SUBJECT_SKEY] = self.subject_uri(iss, sub)
     ds.save()
Exemplo n.º 34
0
 def _add_session(self, sid, **attrs):
     session = DetachedSession(self.env, sid)
     for name, value in attrs.iteritems():
         session[name] = value
     session.save()
Exemplo n.º 35
0
 def _add_session(self, sid, **attrs):
     session = DetachedSession(self.env, sid)
     for name, value in attrs.iteritems():
         session[name] = value
     session.save()
Exemplo n.º 36
0
    def _do_process(self, req):
        """Handle the redirect from the OpenID server.
        """
        db = self.env.get_db_cnx()
        oidconsumer, session = 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'

            # 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.split('@',1)[0].replace('.', ' ').title())

            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 email and self.re_email_white_list:
                self.env.log.debug("Filtering email '%s' through email white-list." % email)
                allowed = False
                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:
                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)
                result = json.load(urllib.urlopen(url))
                if not result[self.check_list_key]:
                    allowed = False
                elif self.check_list_username:
                    new_user = result[self.check_list_username]
                    if new_user:
                        remote_user = new_user

            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

                req.session[self.openid_session_identity_url_key] = info.identity_url
                if email:
                    req.session['email'] = email
                if fullname:
                    req.session['name'] = fullname

                self._commit_session(session, req)

                if req.session.get('name'):
                    authname = req.session['name']
                    if self.combined_username:
                        authname = '%s <%s>' % (authname, remote_user)

                # Possibly lower-case the authname.
                if self.lowercase_authname:
                    authname = authname.lower()

                # Make authname unique in case of collisions
                #
                # XXX: We ought to first look for an existing authenticated
                # ssession 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)

                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.
                        break
                    ds_identity = ds.get(self.openid_session_identity_url_key)
                    if ds_identity == info.identity_url:
                        # No collision
                        break

                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.'
        elif info.status == consumer.CANCEL:
            # cancelled
            message = 'Verification cancelled'
        elif info.status == consumer.SETUP_NEEDED:
            if info.setup_url:
                message = '<a href=%s>Setup needed</a>' % (
                    quoteattr(info.setup_url),)
            else:
                # This means auth didn't succeed, but you're welcome to try
                # non-immediate mode.
                message = 'Setup needed'
        else:
            # Either we don't understand the code or there is no
            # openid_url included with the error. Give a generic
            # failure message. The library should supply debug
            # information in a log.
            message = 'Verification failed.'

        self._commit_session(session, req)

        add_stylesheet(req, 'authopenid/css/openid.css')
        add_script(req, 'authopenid/js/openid-jquery.js')
        return 'openidlogin.html', {
            'images': req.href.chrome('authopenid/images') + '/',
            'action': req.href.openidverify(),
            'message': message,
            'signup': self.signup_link,
            'whatis': self.whatis_link,
            'css_class': css_class,
            'providers_regexp': self.providers_regexp,
            'custom_provider_name': self.custom_provider_name,
            'custom_provider_label': self.custom_provider_label,
            'custom_provider_url': self.custom_provider_url,
            'custom_provider_image': self.custom_provider_image,
            'custom_provider_size': self.custom_provider_size,
            }, None
Exemplo n.º 37
0
 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'
Exemplo n.º 38
0
 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'
Exemplo n.º 39
0
 def make_session(sid, last_visit):
     monkeypatch.setattr("time.time", lambda: last_visit)
     ds = DetachedSession(env, sid)
     ds['bar'] = 'baz'
     ds.save()
Exemplo n.º 40
0
 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'
Exemplo n.º 41
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')
     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')
Exemplo n.º 42
0
 def associate_session(self, authname, iss, sub):
     ds = DetachedSession(self.env, authname)
     ds[self.SUBJECT_SKEY] = self.subject_uri(iss, sub)
     ds.save()