コード例 #1
0
    def test_change_password(self, remember):
        from webidentity.models import PasswordReset
        from webidentity.models import User

        remember.return_value = [('X-Login', 'john.doe')]
        user = User(u'john.doe', u'secret', u'*****@*****.**')
        reset = PasswordReset(1, datetime.now() + timedelta(days=7), u'uniquetoken')

        session = DBSession()
        session.add(reset)
        session.add(user)
        session.flush()

        self.assertEquals(1, user.id)
        self.assertEquals(1, session.query(PasswordReset)\
            .filter(PasswordReset.user_id == 1)\
            .filter(PasswordReset.token == u'uniquetoken')\
            .filter(PasswordReset.expires >= datetime.now())\
            .count())

        view = self._makeView(post={
            'token': 'uniquetoken',
            'password': '******',
            'confirm_password': '******'})
        response = view.change_password()
        self.assertEquals(dict(response.headers), {
            'Content-Length': '0',
            'X-Login': '******',
            'Content-Type': 'text/html; charset=UTF-8',
            'Location': 'http://example.com'})
        # Check that the password was changed
        self.failUnless(session.query(User).get(1).check_password('abc123'))
        # Check that the reset request was deleted
        self.assertEquals(0, session.query(PasswordReset).count())
コード例 #2
0
    def test_log_activity__anonymous(self):
        from webidentity.views.user import log_activity

        session = DBSession()
        self.assertEquals(0, session.query(Activity).count())

        request = testing.DummyRequest()
        log_activity(request, Activity.LOGIN)
        self.assertEquals(0, session.query(Activity).count())
コード例 #3
0
    def test_prune_expired__empty(self):
        from webidentity.models import PasswordReset

        view = self._makeView()
        session = DBSession()

        self.assertEquals(0, session.query(PasswordReset).count())
        view.prune_expired()
        self.assertEquals(0, session.query(PasswordReset).count())
コード例 #4
0
    def test_prune_expired(self):
        from webidentity.models import PasswordReset

        view = self._makeView()
        session = DBSession()

        session.add(PasswordReset(1, datetime.now() - timedelta(days=30)))
        session.add(PasswordReset(2, datetime.now() + timedelta(days=30)))

        self.assertEquals(2, session.query(PasswordReset).count())
        view.prune_expired()
        self.assertEquals(1, session.query(PasswordReset).count())
        self.assertEquals(2, session.query(PasswordReset).first().user_id)
コード例 #5
0
    def test_password_change_form__invalid_user(self):
        from webidentity.models import PasswordReset
        from webidentity.models import User

        view = self._makeView()
        reset = PasswordReset(1, datetime.now() + timedelta(days=7))
        session = DBSession()
        session.add(reset)

        self.assertEquals(1, session.query(PasswordReset).filter_by(token=reset.token).count())
        self.assertEquals(None, session.query(User).get(1))

        view.request.matchdict['token'] = reset.token
        self.assertRaises(NotFound, view.password_change_form)
コード例 #6
0
def verify_accounts():
    """Verifies the create accounts against the password file."""
    engine = engine_from_config(get_config(), 'sqlalchemy.')
    initialize_sql(engine, False)
    session = DBSession()

    if len(sys.argv) < 3:
        print "Usage: {0} <config> <data>".format(sys.argv[0])
        sys.exit(1)

    users = dict((user.username, user) for user in session.query(User).all())
    failed = 0

    print "Checking.."
    passwords = open(sys.argv[2]).readlines()
    for line in passwords:
        username, password = line.strip().split('|', 1)
        if username not in users:
            print "Unknown user:"******"Invalid password for user", username
            failed += 1

    print "Checked", len(passwords), "passwords against", len(users), "users."
    print "Failures", failed
    if failed == 0:
        print "All OK"
    else:
        print "Data inconsistency, please investigate!"
コード例 #7
0
    def test_login__form_submission__success_with_identity_wo_scheme(self, remember):
        from webidentity.views.login import login
        from webidentity.models import User
        session = DBSession()
        session.add(User(u'john.doe', u'secret', u'*****@*****.**'))
        self.assertEquals(
            session.query(User).filter_by(username=u'john.doe').first().email,
            u'*****@*****.**')

        remember.return_value = [('X-Login', 'john.doe')]
        request = testing.DummyRequest(environ={
            'wsgi.url_scheme': 'http',
        })
        token = request.session.new_csrf_token()
        request.POST = {
            'form.submitted': u'1',
            'login': u'example.com/id/john.doe',
            'password': u'secret',
            'csrf_token': token,
        }

        response = login(request)
        self.assertEquals(dict(response.headers), {
            'Content-Length': '0',
            'Content-Type': 'text/html; charset=UTF-8',
            'Location': 'http://example.com',
            'X-Login': u'john.doe'})
        self.assertEquals(request.session.pop_flash(), [u'You have successfully logged in.'])
コード例 #8
0
def yadis(request):
    """Serves the YADIS XRDS documents to facilitate service discovery."""
    OPENID_AX = 'http://openid.net/srv/ax/1.0'

    if request.matchdict.get('local_id', '').strip():
        session = DBSession()
        account = session.query(User).filter(User.username == request.matchdict['local_id']).first()
        if account is None:
            raise NotFound()

        # User specific XRDS document
        options = {
            'service_types': [discover.OPENID_2_0_TYPE, OPENID_AX, sreg.ns_uri],
            'local_id': identity_url(request, request.matchdict['local_id']),
            'uri': route_url('openid_endpoint', request),
        }
    else:
        # Server XRDS document
        options = {
            'service_types': [discover.OPENID_IDP_2_0_TYPE, OPENID_AX, sreg.ns_uri],
            'uri': route_url('openid_endpoint', request),
        }

    response = render_to_response('templates/openid_yadis.pt', options, request)
    response.content_type = 'application/xrds+xml'

    return response
コード例 #9
0
    def test_login__form_submission__invalid_password(self):
        from webidentity.views.login import login
        from webidentity.models import User
        session = DBSession()
        session.add(User(u'john.doe', u'secret', u'*****@*****.**'))
        self.assertEquals(
            session.query(User).filter_by(username=u'john.doe').first().email,
            u'*****@*****.**')

        request = testing.DummyRequest(environ={
            'wsgi.url_scheme': 'http',
        })
        token = request.session.new_csrf_token()
        request.POST = {
            'form.submitted': u'1',
            'login': u'john.doe',
            'password': u'thisiswrong',
            'csrf_token': token,
        }

        options = login(request)
        self.assertEquals(options, {
            'title': u'Login',
            'reset_url': 'http://example.com/reset-password',
            'action_url': 'http://example.com/login',
            'login': u'john.doe',
            'csrf_token': token})
コード例 #10
0
    def test_log_activity__with_url(self):
        from webidentity.views.user import log_activity

        self.config.testing_securitypolicy(userid=u"dokai")
        session = DBSession()
        self.assertEquals(0, session.query(Activity).count())
        user = User("dokai", "secret", "*****@*****.**")
        session.add(user)

        request = testing.DummyRequest(environ={"REMOTE_ADDR": "1.2.3.4"}, cookies={"auth_tkt": "sessiontoken"})
        log_activity(request, Activity.LOGIN, "http://www.rp.com")

        self.assertEquals(1, session.query(Activity).count())
        log_entry = session.query(Activity).first()
        self.assertEquals(user.id, log_entry.user_id)
        self.assertEquals("1.2.3.4", log_entry.ipaddr)
        self.assertEquals("sessiontoken", log_entry.session)
        self.assertEquals(Activity.LOGIN, log_entry.action)
        self.assertEquals("http://www.rp.com", log_entry.url)
コード例 #11
0
def confirm_login_success(request):
    session = DBSession()
    result = session.query(CheckIdRequest)\
                .filter_by(key=request.environ.get('repoze.browserid', ''))\
                .first()

    if result is not None:
        openid_request = result.request
        session.delete(result)
        return auth_decision(request, openid_request)

    raise NotFound('Unknown OpenID request')
コード例 #12
0
    def test_send_confirmation_message(self, send_mail):
        from webidentity.models import PasswordReset
        from webidentity.models import User
        from email.message import Message

        self.config.add_settings(DUMMY_SETTINGS)
        session = DBSession()
        user = User(u'john.doe', u'secret', u'*****@*****.**')
        session.add(user)
        self.assertEquals(u'john.doe', session.query(User).get(1).username)

        view = self._makeView(post={'username': u'john.doe'})
        response = view.send_confirmation_message()

        self.assertEquals(1, session.query(PasswordReset).filter_by(user_id=1).count())
        self.assertEquals(response.location, 'http://example.com')
        self.assertEquals(view.request.session.pop_flash(),
            [u'Password retrieval instructions have been emailed to you.'])
        self.assertEquals(send_mail.call_args[0][0], u'*****@*****.**')
        self.assertEquals(send_mail.call_args[0][1], [u'*****@*****.**'])
        self.failUnless(isinstance(send_mail.call_args[0][2], Message))
コード例 #13
0
    def test_password_change_form(self):
        from webidentity.models import PasswordReset
        from webidentity.models import User

        view = self._makeView()
        session = DBSession()

        user = User(u'john.doe', u'secret', u'*****@*****.**')
        session.add(user)

        reset = PasswordReset(1, datetime.now() + timedelta(days=7), u'uniquetoken')
        session.add(reset)

        self.assertEquals(1, session.query(PasswordReset).filter_by(token=reset.token).count())
        self.assertEquals(u'john.doe', session.query(User).get(user.id).username)

        view.request.matchdict['token'] = reset.token
        self.assertEquals(view.password_change_form(), {
            'action_url': 'http://example.com/reset-password/process',
            'token': u'uniquetoken',
            'title': u'Change password'})
コード例 #14
0
    def test_change_password__invalid_user(self):
        from webidentity.models import PasswordReset

        reset = PasswordReset(1, datetime.now() + timedelta(days=7), u'uniquetoken')
        session = DBSession()
        session.add(reset)

        self.assertEquals(1, session.query(PasswordReset)\
            .filter(PasswordReset.token == u'uniquetoken')\
            .filter(PasswordReset.expires >= datetime.now())\
            .count())

        view = self._makeView(post={
            'token': 'uniquetoken',
            'password': '******',
            'confirm_password': '******'})
        self.assertRaises(NotFound, view.change_password)
コード例 #15
0
    def DISABLED__test_cascading_delete(self):
        from webidentity.models import Persona
        from webidentity.models import User
        from webidentity.models import UserAttribute
        from webidentity.models import VisitedSite

        user = User(u'john.doe', u'secret', u'*****@*****.**')
        user.personas.append(
            Persona(u'Test persönä', attributes=[
                UserAttribute(type_uri, value)
                for type_uri, value
                in DUMMY_USER_ATTRIBUTES.iteritems()]))
        user.personas.append(
            Persona(u'Reversed persönä', attributes=[
                UserAttribute(type_uri, ''.join(reversed(value)))
                for type_uri, value
                in DUMMY_USER_ATTRIBUTES.iteritems()]))

        site1 = VisitedSite('http://www.rp.com', remember=False)
        site2 = VisitedSite('http://www.plone.org', remember=True)
        site2.persona = user.personas[0]

        user.visited_sites.append(site1)
        user.visited_sites.append(site2)

        session = DBSession()
        session.add(user)
        session.flush()

        self.assertEquals(2, len(session.query(User).get(1).personas))
        self.assertEquals(2, len(session.query(User).get(1).visited_sites))
        self.assertEquals(6, len(session.query(User).get(1).personas[0].attributes))

        session.query(User).filter_by(username=u'john.doe').delete()
        session.flush()

        self.assertEquals(0, session.query(User).count())
        self.assertEquals(0, session.query(Persona).count())
        self.assertEquals(0, session.query(UserAttribute).count())
        self.assertEquals(0, session.query(VisitedSite).count())
コード例 #16
0
def get_ax_attributes(request):
    """Returns a dictionary of type_uri => value mappings containing the AX
    attributes for the currently authenticated user.
    """
    user = authenticated_user(request)
    if user is None:
        return {}

    session = DBSession()
    # TODO: Should we convert persona_id to an integer first?
    persona = session.query(Persona)\
                .join(User)\
                .filter(User.id == user.id)\
                .filter(Persona.id == request.params.get('persona', ''))\
                .first()
    if persona is None:
        return {}

    # Get the attribute values from the selected persona.
    return dict((a.type_uri, a.value) for a in persona.attributes)
コード例 #17
0
def identity(request):
    """OpenID identity page for a user.

    The URL this page is served as is the OpenID identifier for the user and
    the contents contain the required information for service discovery.
    """
    if request.matchdict.get('local_id', '').strip():
        session = DBSession()
        account = session.query(User)\
                    .filter(User.username == request.matchdict['local_id'])\
                    .first()
        if account is None:
            raise NotFound()

        return {
            'title': _(u'Identity page'),
            'openid_endpoint': route_url('openid_endpoint', request),
            'xrds_location': route_url('yadis_user', request, local_id=request.matchdict['local_id']),
            'identity': identity_url(request, request.matchdict['local_id']),
            }
    else:
        raise NotFound()
コード例 #18
0
def login(request):
    """Renders a login form and logs in a user if given the correct
    credentials.
    """
    session = DBSession()
    login_url = route_url('login', request)

    login = u''

    if 'form.submitted' in request.POST:
        login = request.POST['login']

        if request.session.get_csrf_token() != request.POST.get('csrf_token'):
            raise Forbidden(u'CSRF attempt detected.')
        else:
            # Allow the use of the full identity URL.
            username = extract_local_id(request, login, relaxed=True)
            if len(username) == 0:
                # Fallback to the the local id.
                username = login

            user = session.query(User).filter_by(username=username).first()
            password = request.POST['password']

            if user is not None and user.check_password(password):
                headers = remember(request, user.username)
                request.session.flash(_(u'You have successfully logged in.'))
                return HTTPFound(location=request.application_url, headers=headers)

            request.session.flash(_(u'Login failed'))

    return {
        'title': _(u'Login'),
        'action_url': login_url,
        'login': login,
        'reset_url': route_url('reset_password', request),
        'csrf_token': request.session.get_csrf_token(),
    }
コード例 #19
0
class PasswordResetView(object):
    """Password reset logic."""

    def __init__(self, request):
        self.request = request
        self.session = DBSession()
        self.prune_expired()

    def prune_expired(self):
        """Prunes password reset requests that have expired."""
        self.session.query(PasswordReset)\
                .filter(PasswordReset.expires < datetime.now())\
                .delete()

    def render_form(self):
        """Renders the password reset form."""
        return {
            'action_url': route_url('reset_password_initiate', self.request),
            'title': _(u'Reset password'),
        }

    def password_change_form(self):
        """Renders the form for changing a password for a valid token."""
        reset = self.session.query(PasswordReset)\
            .filter(PasswordReset.token == self.request.matchdict['token'])\
            .filter(PasswordReset.expires >= datetime.now())\
            .first()
        if reset is None:
            # No matching password reset found
            raise NotFound()

        user = self.session.query(User).get(reset.user_id)
        if user is None:
            raise NotFound()

        return {
            'action_url': route_url('reset_password_process', self.request),
            'title': _(u'Change password'),
            'token': reset.token,
            }

    def send_confirmation_message(self):
        """Sends an email confirmation message to the user."""
        username = self.request.POST.get('username', '').strip()
        redirect_url = route_url('reset_password', self.request)
        if not username:
            self.request.session.flash(_(u'Please supply a username.'))
        else:
            user = self.session.query(User).filter(User.username == username).first()
            if user is None:
                self.request.session.flash(_(u'The given username does not match any account.'))
            else:
                # Create a password reset request that is valid for 24 hours
                reset = PasswordReset(user.id, datetime.now() + timedelta(hours=24))
                self.session.add(reset)
                message = self.create_message(user, reset)
                from_address = self.request.registry.settings['webidentity_from_address'].strip()
                send_mail(from_address, [user.email], message)
                self.request.session.flash(_(u'Password retrieval instructions have been emailed to you.'))
                redirect_url = self.request.application_url

        return HTTPFound(location=redirect_url)

    def create_message(self, user, reset):
        """Returns an email.message.Message object representing the password
        reset message.
        """
        from_address = self.request.registry.settings['webidentity_from_address'].strip()
        date_format = self.request.registry.settings['webidentity_date_format'].strip()
        locale = get_localizer(self.request)
        subject = locale.translate(
            _(u'Password reset for ${identity}',
            mapping={'identity': identity_url(self.request, user.username)}))

        message = Message()
        message['From'] = Header(from_address, 'utf-8')
        message['To'] = Header(u'{0} <{1}>'.format(user.username, user.email), 'utf-8')
        message['Subject'] = Header(subject, 'utf-8')

        message.set_payload(locale.translate(_(
            u'password-reset-email',
            default=textwrap.dedent(u'''
            Hi ${username}

            A password retrieval process has been initiated for your OpenID
            identity

              ${identity}

            If the process was initiated by you you can continue the process
            of resetting your password by opening the following link in your
            browser

              ${reset_url}

            The link will will expire at ${expiration}.

            If you did not initiate this request you can just ignore this
            email. Your password has not been changed.
            ''').lstrip(),
            mapping=dict(
                username=user.username,
                identity=identity_url(self.request, user.username),
                expiration=reset.expires.strftime(date_format),
                reset_url=route_url('reset_password_token', self.request, token=reset.token)))))

        return message

    def change_password(self):
        """Changes the password for a user."""
        token = self.request.POST.get('token', '').strip()

        if token:
            password = self.request.POST.get('password', '')
            confirm_password = self.request.POST.get('confirm_password', '')
            if len(password.strip()) < 5:
                self.request.session.flash(_(u'Password must be at least five characters long'))
                return HTTPFound(
                    location=route_url('reset_password_token', self.request, token=token))
            elif password != confirm_password:
                self.request.session.flash(_(u'Given passwords do not match'))
                return HTTPFound(
                    location=route_url('reset_password_token', self.request, token=token))
            else:
                reset = self.session.query(PasswordReset)\
                    .filter(PasswordReset.token == token)\
                    .filter(PasswordReset.expires >= datetime.now())\
                    .first()
                if reset is None:
                    raise NotFound()

                user = self.session.query(User).get(reset.user_id)
                if user is None:
                    raise NotFound()

                # Update the user password
                user.password = password
                self.session.add(user)
                self.session.delete(reset)

                self.request.session.flash(_(u'Password changed.'))
                headers = remember(self.request, user.username)
                return HTTPFound(location=self.request.application_url, headers=headers)

        raise NotFound()
コード例 #20
0
        return HTTPBadRequest('Invalid OpenID request')

    # The two cases that we need to handle explicitly to implement our
    # provider logic.
    if openid_request.mode in ('checkid_immediate', 'checkid_setup'):
        # Determine if the user is authorized already. This requires that
        #  - The user is logged into the provider
        #  - The claimed identity matches the user's own identity
        #  - The user has approved the connection previously for new requests.
        authorized = False
        attributes = {}
        if user is not None and openid_request.identity == identity_url(request, user):
            session = DBSession()
            visit = session.query(VisitedSite)\
                        .join(User)\
                        .filter(User.username == user)\
                        .filter(VisitedSite.trust_root == openid_request.trust_root)\
                        .first()
            if visit is not None and visit.remember:
                authorized = True
                if visit.persona is not None:
                    attributes = dict((a.type_uri, a.value) for a in visit.persona.attributes)

        if authorized:
            # The user has been authorized previously.
            openid_response = openid_request.answer(True)
            add_ax_response(openid_request, openid_response, attributes)
            add_sreg_response(openid_request, openid_response, attributes)
        elif openid_request.immediate:
            # Non-authorized immediate request will fail unconditionally.
            openid_response = openid_request.answer(False)
コード例 #21
0
def auth_decision(request, openid_request):
    """Renders the appropriate page that allows the user to make a decision
    about the OpenID authentication request.

    The possible scenarios are:

    1. The user is logged in to the OP and RP wants OP to select the id
    2. The user is logged in to the OP and her identity matches the claimed id.
    3. The user is logged in to the OP and her identity does not match the claimed id.
    4. The user is not logged in and RP wants OP to select the id.
    5. The user is not logged in and RP sent a claimed id.

    In cases 1) and 2) the user can simply make the decision on allowing the
    authentication to proceed (and AX related decisions.)

    In cases 3), 4) and 5) the user needs to log in to the OP first.
    """
    user = authenticated_user(request)
    expected_user = extract_local_id(request, openid_request.identity)
    session = DBSession()
    log = logging.getLogger('webidentity')

    # Persist the OpenID request so we can continue processing it after the
    # user has authenticated overriding any previous request.
    cidrequest = session.query(CheckIdRequest).get(request.environ['repoze.browserid'])
    if cidrequest is None:
        cidrequest = CheckIdRequest(request.environ['repoze.browserid'], openid_request)
    else:
        # Unconditionally overwrite any existing checkid request.
        cidrequest.request = openid_request
        cidrequest.issued = int(time.time())

    session.add(cidrequest)
    # Clean up dangling requests for roughly 10% of the time.
    # We do not do this on every call to reduce the possibility of a database
    # deadlock as rows need to be locked.
    if random.randint(0, 100) < 10:
        threshold = int(time.time()) - 3600
        session.query(CheckIdRequest).filter(CheckIdRequest.issued < threshold).delete()
        log.info('Cleaned up dangling OpenID authentication requests.')

    options = {
        'title': _(u'Approve OpenID request?'),
        'action_url': route_url('openid_confirm', request),
        'trust_root': openid_request.trust_root,
        #'reset_url': route_url('reset_password', request),
    }

    if user is not None:
        ax_request = ax.FetchRequest.fromOpenIDRequest(openid_request)
        sreg_request = sreg.SRegRequest.fromOpenIDRequest(openid_request)

        if ax_request is not None or sreg_request is not None:
            # Prepare the user attributes for an SReg/AX request.

            # Mapping of attributes the RP has requested from the user.
            requested_attributes = {}

            if sreg_request.wereFieldsRequested():
                requested_attributes.update(dict(
                    (SREG_TO_AX.get(field), field in sreg_request.required)
                    for field
                    in sreg_request.allRequestedFields()))

            # The AX request takes precedence over SReg if both are requested.
            if ax_request is not None:
                requested_attributes.update(dict(
                    (type_uri, attrinfo.required)
                    for type_uri, attrinfo
                    in ax_request.requested_attributes.iteritems()))

            # Set of attributes that the RP has marked mandatory
            required_attributes = set(
                type_uri
                for type_uri, required
                in requested_attributes.iteritems()
                if required)

            for persona in user.personas:
                available_attributes = set(a.type_uri for a in persona.attributes)
                options.setdefault('personas', []).append({
                    'id': persona.id,
                    'name': persona.name,
                    'attributes': [
                        dict(type_uri=a.type_uri, value=a.value)
                        for a in persona.attributes
                        if a.type_uri in requested_attributes],
                    'missing': required_attributes - available_attributes,
                })

        if openid_request.idSelect():
            # Case 1.
            # The Relying Party has requested us to select an identifier.
            options.update({
                'identity': identity_url(request, user.username),
                'csrf_token': request.session.get_csrf_token(),
            })
            request.add_response_callback(disable_caching)
            return render_to_response('templates/openid_confirm.pt', options, request)
        elif user.username == expected_user:
            # Case 2.
            # A logged-in user that matches the user in the claimed identifier.
            options.update({
                'identity': openid_request.identity,
                'csrf_token': request.session.get_csrf_token(),
            })
            request.add_response_callback(disable_caching)
            return render_to_response('templates/openid_confirm.pt', options, request)
        else:
            # Case 3
            # TODO: Implement case 3
            raise NotImplementedError
    else:
        # Cases 4 and 5.
        options.update({
            'action_url': route_url('openid_confirm_login', request),
            'login_name': expected_user if not openid_request.idSelect() else None,
            'csrf_token': request.session.get_csrf_token(),
        })
        return render_to_response('templates/openid_confirm_login.pt', options, request)
コード例 #22
0
def confirm(request):
    """Confirmation for an OpenID authentication request."""
    user = authenticated_user(request)
    if user is not None and 'form.submitted' in request.params:
        if request.POST.get('csrf_token') != request.session.get_csrf_token():
            raise Forbidden

        session = DBSession()
        result = session.query(CheckIdRequest)\
                    .filter_by(key=request.environ.get('repoze.browserid', ''))\
                    .first()

        if result is not None:
            openid_request = result.request
            session.delete(result)
        else:
            # TODO: Return an error message
            return HTTPBadRequest('Invalid confirmation request')

        if 'accept' in request.params:
            ax_attributes = get_ax_attributes(request)

            visit = session.query(VisitedSite)\
                        .join(User)\
                        .filter(User.id == user.id)\
                        .filter(VisitedSite.trust_root == openid_request.trust_root)\
                        .first()
            if visit is None:
                # This is the first time the user is visiting this RP
                visit = VisitedSite(openid_request.trust_root)
                user.visited_sites.append(visit)

            visit.remember = 'remember' in request.params

            try:
                persona_id = int(request.params.get('persona'))
                # Make sure that the referenced persona actually belongs to the user
                persona = session.query(Persona)\
                            .join(User)\
                            .filter(User.id == user.id)\
                            .filter(Persona.id == persona_id)\
                            .first()
                if persona is not None:
                    visit.persona = persona
            except (TypeError, ValueError):
                pass

            session.add(visit)

            identity = identity_url(request, user.username)
            openid_response = openid_request.answer(True, identity=identity)
            add_ax_response(openid_request, openid_response, ax_attributes)
            add_sreg_response(openid_request, openid_response, ax_attributes)

            if visit.remember:
                log_activity(request, Activity.AUTHORIZE, openid_request.trust_root)
            else:
                log_activity(request, Activity.AUTHORIZE_ONCE, openid_request.trust_root)

        else:
            log_activity(request, Activity.DENY, openid_request.trust_root)
            openid_response = openid_request.answer(False)

        store = AlchemyStore(session)
        openid_server = server.Server(store, route_url('openid_endpoint', request))

        response = webob_response(openid_server.encodeResponse(openid_response), request)
        response.headerlist.extend(forget(request))

        return response

    return HTTPBadRequest('Invalid confirmation request')