예제 #1
0
def new_authorized_apps_collection(db):
    """Move the authorized_apps information from the users collection
    to an authorized_apps collection.
    """
    app_cache = {}
    auth = Authorizator(db)
    scopes = ['read-passwords', 'write-passwords', 'read-userinfo']
    for user in db.users.find():
        authorized_apps = user['authorized_apps']
        # create an authorized_apps document for every authorized app
        for app_id in authorized_apps:
            if app_id not in app_cache:
                app_cache[app_id] = db.applications.find_one({'_id': app_id})
            app = app_cache[app_id]
            credentials = {
                'client_id': app['client_id'],
                'user': user,
                'redirect_uri': app['callback_url'],
                'response_type': 'code',
            }
            auth.store_user_authorization(scopes, credentials)
            safe_print('Storing authorized app "%s" for user %s' % (
                app['client_id'],
                get_user_display_name(user),
            ))

    # remove the authorized_apps attribute from all users
    db.users.update({}, {'$unset': {'authorized_apps': ''}}, multi=True)
예제 #2
0
def revoke_application(request):
    assert_authenticated_user_is_registered(request)

    try:
        app_id = bson.ObjectId(request.matchdict['app'])
    except bson.errors.InvalidId:
        return HTTPBadRequest(body='Invalid application id')

    app = request.db.applications.find_one(app_id)
    if app is None:
        return HTTPNotFound()

    authorizator = Authorizator(request.db, app)

    if not authorizator.is_app_authorized(request.user):
        return HTTPUnauthorized()

    if 'submit' in request.POST:
        authorizator.remove_user_authorization(request.user)

        request.session.flash(
            _('The access to application ${app} has been revoked',
              mapping={'app': app['name']}),
            'success',
            )
        return HTTPFound(
            location=request.route_path('oauth2_authorized_applications'))

    return {'app': app}
예제 #3
0
    def test_revoke_application(self):
        # this view required authentication
        res = self.testapp.get('/oauth2/applications/xxx/revoke')
        self.assertEqual(res.status, '200 OK')
        res.mustcontain('Log in')

        # Log in
        user_id = self.db.users.insert({
            'twitter_id': 'twitter1',
            'screen_name': 'John Doe',
            'first_name': 'John',
            'last_name': 'Doe',
            'email': '*****@*****.**',
        })
        self.testapp.get('/__login/' + str(user_id))

        res = self.testapp.get('/oauth2/applications/xxx/revoke',
                               status=400)
        self.assertEqual(res.status, '400 Bad Request')
        res.mustcontain('Invalid application id')

        res = self.testapp.get(
            '/oauth2/applications/000000000000000000000000/revoke',
            status=404)
        self.assertEqual(res.status, '404 Not Found')

        # create a valid app
        app_id = self.db.applications.insert({
            'owner': bson.ObjectId(),
            'name': 'Test Application',
            'main_url': 'http://example.com',
            'callback_url': 'http://example.com/callback',
            'client_id': '123456',
            'client_secret': 'secret',
        })

        authorizator = Authorizator(self.db)
        credentials = {
            'client_id': '123456',
            'user': {'_id': user_id},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        }
        authorizator.store_user_authorization(['read-passwords'], credentials)

        res = self.testapp.get('/oauth2/applications/%s/revoke' % str(app_id))
        self.assertEqual(res.status, '200 OK')
        res.mustcontain('Revoke authorization to application <span>Test Application</span>')

        res = self.testapp.post('/oauth2/applications/%s/revoke' % str(app_id), {
            'submit': 'Yes, I am sure',
        })
        self.assertEqual(res.status, '302 Found')
        self.assertEqual(res.location, 'http://localhost/oauth2/authorized-applications')
        self.assertFalse(authorizator.is_app_authorized(['read-passwords'],
                                                        credentials))
예제 #4
0
    def test_some_users(self):
        authorizator = Authorizator(self.db)

        app1_id = self.db.applications.insert({
            'client_id': 'app1',
            'callback_url': 'https://example.com/callback/1',
        })
        app2_id = self.db.applications.insert({
            'client_id': 'app2',
            'callback_url': 'https://example.com/callback/2',
        })

        u1_id = self.db.users.insert({
            'first_name': 'John',
            'last_name': 'Doe',
            'email': '*****@*****.**',
            'authorized_apps': [app1_id, app2_id],
        })
        auths = authorizator.get_user_authorizations({'_id': u1_id})
        self.assertEqual(auths.count(), 0)
        u2_id = self.db.users.insert({
            'first_name': 'John2',
            'last_name': 'Doe2',
            'email': '*****@*****.**',
            'send_passwords_periodically': False,
            'authorized_apps': [app1_id],
        })
        auths = authorizator.get_user_authorizations({'_id': u2_id})
        self.assertEqual(auths.count(), 0)

        sys.argv = ['notused', self.conf_file_path, 'new_authorized_apps_collection']
        sys.stdout = StringIO()
        result = migrate()
        self.assertEqual(result, None)
        stdout = sys.stdout.getvalue()
        stdout = sys.stdout.getvalue()
        expected_output = """Storing authorized app "app1" for user John Doe <*****@*****.**>
Storing authorized app "app2" for user John Doe <*****@*****.**>
Storing authorized app "app1" for user John2 Doe2 <*****@*****.**>
"""
        self.assertEqual(stdout, expected_output)

        user1 = self.db.users.find_one({'_id': u1_id})
        self.assertFalse('authorized_apps' in user1)
        auths = authorizator.get_user_authorizations({'_id': u1_id})
        self.assertEqual(auths.count(), 2)

        user2 = self.db.users.find_one({'_id': u2_id})
        self.assertFalse('authorized_apps' in user2)
        auths = authorizator.get_user_authorizations({'_id': u2_id})
        self.assertEqual(auths.count(), 1)
예제 #5
0
    def test_already_authorized_app(self):
        user_id = self._login()
        self._create_client()

        authorizator = Authorizator(self.db)
        auths = authorizator.get_user_authorizations({'_id': user_id})
        self.assertEqual(auths.count(), 0)

        # do an initial authorization
        res = self.testapp.get('/oauth2/endpoints/authorization', {
            'response_type': 'code',
            'client_id': '123456',
            'redirect_uri': 'https://example.com/callback',
        })
        self.assertEqual(res.status, '200 OK')

        res = self.testapp.post('/oauth2/endpoints/authorization', {
            'submit': 'Authorize',
            'response_type': 'code',
            'client_id': '123456',
            'redirect_uri': 'https://example.com/callback',
            'scope': 'read-passwords',
        })
        self.assertEqual(res.status, '302 Found')

        auths = authorizator.get_user_authorizations({'_id': user_id})
        self.assertEqual(auths.count(), 1)

        # Now do a second authorization
        res = self.testapp.get('/oauth2/endpoints/authorization', {
            'response_type': 'code',
            'client_id': '123456',
            'redirect_uri': 'https://example.com/callback',
        })
        self.assertEqual(res.status, '302 Found')

        auths = authorizator.get_user_authorizations({'_id': user_id})
        self.assertEqual(auths.count(), 1)

        grants = self.db.authorization_codes.find({
            'client_id': '123456',
            'user': user_id,
        })

        # There are two grants now
        self.assertEqual(grants.count(), 2)
        code = grants[1]['code']
        location = 'https://example.com/callback?code=%s' % code
        self.assertEqual(res.location, location)
예제 #6
0
    def test_non_authorized_app_yet(self):
        user_id = self._login()
        self._create_client()

        authorizator = Authorizator(self.db)
        auths = authorizator.get_user_authorizations({'_id': user_id})
        self.assertEqual(auths.count(), 0)

        res = self.testapp.get('/oauth2/endpoints/authorization', {
            'response_type': 'code',
            'client_id': '123456',
            'redirect_uri': 'https://example.com/callback',
        })
        self.assertEqual(res.status, '200 OK')
        res.mustcontain('Authorize Application')
        res.mustcontain('Permissions:')
        res.mustcontain('Access your passwords')
        res.mustcontain('Allow access')
        res.mustcontain('No, thanks')
        res.mustcontain('You can revoke this authorization in the future.')

        res = self.testapp.post('/oauth2/endpoints/authorization', {
            'submit': 'Authorize',
            'response_type': 'code',
            'client_id': '123456',
            'redirect_uri': 'https://example.com/callback',
            'scope': 'read-passwords',
        })
        self.assertEqual(res.status, '302 Found')

        # Check that the app is authorized now
        auths = authorizator.get_user_authorizations({'_id': user_id})
        self.assertEqual(auths.count(), 1)
        auth = auths[0]
        self.assertEqual(auth['redirect_uri'], 'https://example.com/callback')
        self.assertEqual(auth['response_type'], 'code')
        self.assertEqual(auth['client_id'], '123456')
        self.assertEqual(auth['scope'], 'read-passwords')
        self.assertEqual(auth['user'], user_id)

        # Check the right redirect url
        grant = self.db.authorization_codes.find_one({
            'client_id': '123456',
            'user': user_id,
        })
        self.assertNotEqual(grant, None)
        code = grant['code']
        location = 'https://example.com/callback?code=%s' % code
        self.assertEqual(res.location, location)
예제 #7
0
def merge_users(db, user1, user2):
    # move all passwords of user2 to user1
    db.passwords.update({'owner': user2['_id']}, {
        '$set': {
            'owner': user1['_id'],
        },
    }, multi=True)

    # move authorized_apps from user2 to user1
    authorizator = Authorizator(db)
    for auth in authorizator.get_user_authorizations(user2):
        credentials = {
            'client_id': auth['client_id'],
            'user': user1,
            'redirect_uri': auth['redirect_uri'],
            'response_type': auth['response_type'],
        }
        scopes = auth['scope'].split(' ')
        authorizator.store_user_authorization(scopes, credentials)
    authorizator.remove_all_user_authorizations(user2)

    updates = {}
    # copy the providers
    for provider in get_available_providers():
        key = provider + '_id'
        if key in user2 and key not in user1:
            sets = updates.setdefault('$set', {})
            sets[key] = user2[key]

    db.users.update({'_id': user1['_id']}, updates)

    # remove user2
    db.users.remove(user2['_id'])
    def test_authorizator(self):
        app = {"_id": "app1"}
        authorizator = Authorizator(self.db, app)
        self.assertTrue(isinstance(authorizator.auth_codes, AuthorizationCodes))
        self.assertTrue(isinstance(authorizator.access_codes, AccessCodes))
        user = {"name": "John Doe", "authorized_apps": []}
        self.db.users.insert(user, safe=True)

        self.assertFalse(authorizator.is_app_authorized(user))

        authorizator.store_user_authorization(user)
        user = self.db.users.find_one({"name": "John Doe"})

        self.assertTrue(authorizator.is_app_authorized(user))
        self.assertEqual(user["authorized_apps"], ["app1"])

        authorizator.remove_user_authorization(user)
        user = self.db.users.find_one({"name": "John Doe"})

        self.assertFalse(authorizator.is_app_authorized(user))
        self.assertFalse("app1" in user["authorized_apps"])
    def test_authorizator(self):
        app = {'_id': 'app1'}
        authorizator = Authorizator(self.db, app)
        self.assertTrue(isinstance(authorizator.auth_codes,
                                   AuthorizationCodes))
        self.assertTrue(isinstance(authorizator.access_codes,
                                   AccessCodes))
        user = {'name': 'John Doe', 'authorized_apps': []}
        self.db.users.insert(user, safe=True)

        self.assertFalse(authorizator.is_app_authorized(user))

        authorizator.store_user_authorization(user)
        user = self.db.users.find_one({'name': 'John Doe'})

        self.assertTrue(authorizator.is_app_authorized(user))
        self.assertEqual(user['authorized_apps'], ['app1'])

        authorizator.remove_user_authorization(user)
        user = self.db.users.find_one({'name': 'John Doe'})

        self.assertFalse(authorizator.is_app_authorized(user))
        self.assertFalse('app1' in user['authorized_apps'])
예제 #10
0
 def setUp(self):
     super(AuthorizatorTests, self).setUp()
     self.authorizator = Authorizator(self.db)
예제 #11
0
class AuthorizatorTests(testing.TestCase):

    def setUp(self):
        super(AuthorizatorTests, self).setUp()
        self.authorizator = Authorizator(self.db)

    def test_is_app_authorized_no_authorized_apps(self):
        self.assertFalse(self.authorizator.is_app_authorized([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        }))

    def test_is_app_authorized_different_client_id(self):
        self.db.authorized_apps.insert({
            'client_id': 1,
            'user': 1,
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
            'scope': 'scope1 scope2',
        })
        self.assertFalse(self.authorizator.is_app_authorized([
            'scope1', 'scope2',
        ], {
            'client_id': 2,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        }))

    def test_is_app_authorized_different_user(self):
        self.db.authorized_apps.insert({
            'client_id': 1,
            'user': 1,
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
            'scope': 'scope1 scope2',
        })
        self.assertFalse(self.authorizator.is_app_authorized([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 2},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        }))

    def test_is_app_authorized_different_redirect_uri(self):
        self.db.authorized_apps.insert({
            'client_id': 1,
            'user': 1,
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
            'scope': 'scope1 scope2',
        })
        self.assertFalse(self.authorizator.is_app_authorized([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback-new',
            'response_type': 'code',
        }))

    def test_is_app_authorized_different_response_type(self):
        self.db.authorized_apps.insert({
            'client_id': 1,
            'user': 1,
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
            'scope': 'scope1 scope2',
        })
        self.assertFalse(self.authorizator.is_app_authorized([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'token',
        }))

    def test_is_app_authorized_different_scopes(self):
        self.db.authorized_apps.insert({
            'client_id': 1,
            'user': 1,
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
            'scope': 'scope1 scope2',
        })
        self.assertFalse(self.authorizator.is_app_authorized([
            'scope1', 'scope2', 'scope3',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        }))

    def test_is_app_authorized_everything_equal(self):
        self.db.authorized_apps.insert({
            'client_id': 1,
            'user': 1,
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
            'scope': 'scope1 scope2',
        })
        self.assertTrue(self.authorizator.is_app_authorized([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        }))

    def test_store_user_authorization_no_previous_authorization(self):
        self.assertEqual(self.db.authorized_apps.count(), 0)
        self.authorizator.store_user_authorization([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        })
        self.assertEqual(self.db.authorized_apps.count(), 1)

    def test_store_user_authorization_previous_authorization(self):
        self.assertEqual(self.db.authorized_apps.count(), 0)
        self.authorizator.store_user_authorization([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        })
        self.assertEqual(self.db.authorized_apps.count(), 1)

        # Store the same authorization again
        self.authorizator.store_user_authorization([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        })
        # still only one record
        self.assertEqual(self.db.authorized_apps.count(), 1)

    def test_get_user_authorizations_empty(self):
        auths = self.authorizator.get_user_authorizations({'_id': 1})
        self.assertEqual(auths.count(), 0)

    def test_get_user_authorizations_one_authorization(self):
        self.authorizator.store_user_authorization([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        })
        auths = self.authorizator.get_user_authorizations({'_id': 1})
        self.assertEqual(auths.count(), 1)
        self.assertEqual(auths[0]['client_id'], 1)
        self.assertEqual(auths[0]['redirect_uri'], 'http://example.com/callback')
        self.assertEqual(auths[0]['response_type'], 'code')
        self.assertEqual(auths[0]['scope'], 'scope1 scope2')

    def test_get_user_authorizations_two_authorization(self):
        self.authorizator.store_user_authorization([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        })
        self.authorizator.store_user_authorization([
            'scope1', 'scope2',
        ], {
            'client_id': 2,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback2',
            'response_type': 'code',
        })
        self.authorizator.store_user_authorization([
            'scope1', 'scope2',
        ], {
            'client_id': 2,
            'user': {'_id': 2},
            'redirect_uri': 'http://example.com/callback2',
            'response_type': 'code',
        })
        auths = self.authorizator.get_user_authorizations({'_id': 1})
        self.assertEqual(auths.count(), 2)
        self.assertEqual(auths[0]['client_id'], 1)
        self.assertEqual(auths[0]['redirect_uri'], 'http://example.com/callback')
        self.assertEqual(auths[0]['response_type'], 'code')
        self.assertEqual(auths[0]['scope'], 'scope1 scope2')

        self.assertEqual(auths[1]['client_id'], 2)
        self.assertEqual(auths[1]['redirect_uri'], 'http://example.com/callback2')
        self.assertEqual(auths[1]['response_type'], 'code')
        self.assertEqual(auths[1]['scope'], 'scope1 scope2')

    def test_remove_user_authorization(self):
        auths = self.authorizator.get_user_authorizations({'_id': 1})
        self.assertEqual(auths.count(), 0)
        self.authorizator.store_user_authorization([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback',
            'response_type': 'code',
        })
        auths = self.authorizator.get_user_authorizations({'_id': 1})
        self.assertEqual(auths.count(), 1)
        self.authorizator.remove_user_authorization({'_id': 1}, 1)
        auths = self.authorizator.get_user_authorizations({'_id': 1})
        self.assertEqual(auths.count(), 0)

    def test_remove_all_user_authorizations(self):
        auths = self.authorizator.get_user_authorizations({'_id': 1})
        self.assertEqual(auths.count(), 0)
        self.authorizator.store_user_authorization([
            'scope1', 'scope2',
        ], {
            'client_id': 1,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback/1',
            'response_type': 'code',
        })
        self.authorizator.store_user_authorization([
            'scope1', 'scope2',
        ], {
            'client_id': 2,
            'user': {'_id': 1},
            'redirect_uri': 'http://example.com/callback/2',
            'response_type': 'code',
        })
        auths = self.authorizator.get_user_authorizations({'_id': 1})
        self.assertEqual(auths.count(), 2)
        self.authorizator.remove_all_user_authorizations({'_id': 1})
        auths = self.authorizator.get_user_authorizations({'_id': 1})
        self.assertEqual(auths.count(), 0)
예제 #12
0
def authorization_endpoint(request):
    response_type = request.params.get('response_type')
    if response_type is None:
        return HTTPBadRequest('Missing required response_type')

    if response_type != 'code':
        return HTTPNotImplemented('Only code is supported')

    client_id = request.params.get('client_id')
    if client_id is None:
        return HTTPBadRequest('Missing required client_type')

    app = request.db.applications.find_one({'client_id': client_id})
    if app is None:
        return HTTPNotFound()

    redirect_uri = request.params.get('redirect_uri')
    if redirect_uri is None:
        redirect_uri = app['callback_url']
    else:
        if redirect_uri != app['callback_url']:
            return HTTPBadRequest(
                'Redirect URI does not match registered callback URL')

    scope = request.params.get('scope', DEFAULT_SCOPE)

    state = request.params.get('state')

    user = assert_authenticated_user_is_registered(request)

    authorizator = Authorizator(request.db, app)

    if 'submit' in request.POST:
        if not authorizator.is_app_authorized(request.user):
            authorizator.store_user_authorization(request.user)

        code = authorizator.auth_codes.create(
            request.user['_id'], app['client_id'], scope)
        url = authorizator.auth_codes.get_redirect_url(
            code, redirect_uri, state)
        return HTTPFound(location=url)

    elif 'cancel' in request.POST:
        return HTTPFound(app['main_url'])

    else:
        if authorizator.is_app_authorized(user):
            code = authorizator.auth_codes.create(
                user['_id'], app['client_id'], scope)
            url = authorizator.auth_codes.get_redirect_url(
                code, redirect_uri, state)
            return HTTPFound(location=url)

        else:
            authorship_information = ''
            owner_id = app.get('owner', None)
            if owner_id is not None:
                owner = request.db.users.find_one({'_id': owner_id})
                if owner:
                    email = owner.get('email', None)
                    if email:
                        authorship_information = _('By ${owner}',
                                                   mapping={'owner': email})

            scopes = [SCOPE_NAMES.get(scope, scope)
                      for scope in scope.split(' ')]
            return {
                'response_type': response_type,
                'client_id': client_id,
                'redirect_uri': redirect_uri,
                'scope': scope,
                'state': state,
                'app': app,
                'scopes': scopes,
                'authorship_information': authorship_information,
                }
예제 #13
0
    def test_identity_providers(self):
        # this view required authentication
        res = self.testapp.get('/identity-providers')
        self.assertEqual(res.status, '200 OK')
        res.mustcontain('Log in')

        authorizator = Authorizator(self.db)

        # Log in
        user1_id = self.db.users.insert({
            'twitter_id': 'twitter1',
            'screen_name': 'John Doe',
            'first_name': 'John',
            'last_name': 'Doe',
            'email': '*****@*****.**',
            'email_verified': True,
        })
        authorizator.store_user_authorization(['scope1'], {
            'client_id': 'app1',
            'user': {'_id': user1_id},
            'redirect_uri': 'http://example.com/callback/1',
            'response_type': 'code',
        })
        authorizator.store_user_authorization(['scope1'], {
            'client_id': 'app2',
            'user': {'_id': user1_id},
            'redirect_uri': 'http://example.com/callback/2',
            'response_type': 'code',
        })
        self.testapp.get('/__login/' + str(user1_id))
        self.db.passwords.insert({
            'owner': user1_id,
            'password': '******',
        })

        # one account is not enough for merging
        res = self.testapp.post('/identity-providers', {
            'submit': 'Merge my accounts',
        }, status=400)
        self.assertEqual(res.status, '400 Bad Request')
        res.mustcontain('You do not have enough accounts to merge')

        # so let's create another account with the same email
        user2_id = self.db.users.insert({
            'google_id': 'google1',
            'screen_name': 'John Doe',
            'first_name': 'John',
            'last_name': 'Doe',
            'email': '*****@*****.**',
            'email_verified': True,
        })
        authorizator.store_user_authorization(['scope1'], {
            'client_id': 'app2',
            'user': {'_id': user2_id},
            'redirect_uri': 'http://example.com/callback/2',
            'response_type': 'code',
        })
        authorizator.store_user_authorization(['scope1'], {
            'client_id': 'app3',
            'user': {'_id': user2_id},
            'redirect_uri': 'http://example.com/callback/3',
            'response_type': 'code',
        })
        self.db.passwords.insert({
            'owner': user2_id,
            'password': '******',
        })

        # now the profile view should say I can merge my accounts
        res = self.testapp.get('/identity-providers')
        self.assertEqual(res.status, '200 OK')
        res.mustcontain('You are registered with the following accounts',
                        'Merge my accounts',
                        'If you merge your accounts')

        # if only one account is selected or fake accounts
        # are selected nothing is merged
        res = self.testapp.post('/identity-providers', {
            'account-%s' % str(user1_id): 'on',
            'account-000000000000000000000000': 'on',
            'submit': 'Merge my accounts',
        }, status=302)
        self.assertEqual(res.status, '302 Found')
        self.assertEqual(res.location, 'http://localhost/identity-providers')
        self.assertEqual(2, self.db.users.count())
        self.assertEqual(1, self.db.passwords.find(
            {'owner': user1_id}).count())
        self.assertEqual(1, self.db.passwords.find(
            {'owner': user2_id}).count())

        # let's merge them
        res = self.testapp.post('/identity-providers', {
            'account-%s' % str(user1_id): 'on',
            'account-%s' % str(user2_id): 'on',
            'submit': 'Merge my accounts',
        }, status=302)
        self.assertEqual(res.status, '302 Found')
        self.assertEqual(res.location, 'http://localhost/identity-providers')

        # the accounts have been merged
        self.assertEqual(1, self.db.users.count())
        user1_refreshed = self.db.users.find_one({'_id': user1_id})
        self.assertEqual(user1_refreshed['google_id'], 'google1')
        auths = authorizator.get_user_authorizations(user1_refreshed)
        for real, expected in zip(auths, ['app1', 'app2', 'app3']):
            self.assertEqual(real['client_id'], expected)

        user2_refreshed = self.db.users.find_one({'_id': user2_id})
        self.assertEqual(user2_refreshed, None)

        self.assertEqual(2, self.db.passwords.find(
            {'owner': user1_id}).count())