class MongoDB(object):
    """Simple wrapper to get pymongo real objects from the settings uri"""

    def __init__(self, db_uri=DEFAULT_MONGODB_URI,
                 connection_factory=pymongo.Connection):
        self.db_uri = urlparse.urlparse(db_uri)
        self.connection = connection_factory(
            host=self.db_uri.hostname or DEFAULT_MONGODB_HOST,
            port=self.db_uri.port or DEFAULT_MONGODB_PORT,
            tz_aware=True)

        if self.db_uri.path:
            self.database_name = self.db_uri.path[1:]
        else:
            self.database_name = DEFAULT_MONGODB_NAME

    def get_connection(self):
        return self.connection

    def get_database(self):
        database = self.connection[self.database_name]
        if self.db_uri.username and self.db_uri.password:
            database.authenticate(self.db_uri.username, self.db_uri.password)

        return database def get_available_providers():
    return ('facebook', 'google', 'twitter', 'persona') def get_providers(user, current):
    result = []
    for provider in get_available_providers():
        key = provider + '_id'
        if key in user and user[key] is not None:
            result.append({
                    'name': provider,
                    'is_current': current == provider,
                    })
    return result def get_n_passwords(db, user):
    return db.passwords.find({
            'owner': user.get('_id', None),
            }, safe=True).count() def get_accounts(db, current_user, current_provider):
    email = current_user.get('email', None)
    results = db.users.find({
            'email': email,
            '_id': {'$ne': current_user.get('_id', None)},
            }, safe=True)

    if current_user:
        results = [current_user] + list(results)

    accounts = []
    for user in results:
        providers = get_providers(user, current_provider)
        is_current = current_provider in [p['name'] for p in providers]
        accounts.append({
            'providers': providers,
            'is_current': is_current,
            'passwords': get_n_passwords(db, user),
            'id': str(user['_id']),
            'is_verified': user.get('email_verified', False),
            })
    return accounts def merge_accounts(db, master_user, accounts):
    merged = 0

    for account in accounts:
        user_id = bson.ObjectId(account)
        if master_user['_id'] == user_id:
            continue

        current_user = db.users.find_one({'_id': user_id}, safe=True)
        if current_user is None:
            continue

        merge_users(db, master_user, current_user)

        merged += 1

    return merged 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, safe=True)

    # copy authorized_apps from user2 to user1
    updates = {
        '$addToSet': {
            'authorized_apps': {
                '$each': user2['authorized_apps'],
                },
            },
        }

    # 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, safe=True)

    # remove user2
    db.users.remove(user2['_id']) def notify_admins_of_account_removal(request, user, reason):
    context = {
        'reason': reason or 'no reason was given',
        'user': user,
        'home_link': request.route_url('home'),
    }
    return send_email_to_admins(
        request,
        'yithlibraryserver.user:templates/account_removal_notification',
        context,
        'A user has destroyed his Yith Library account',
    ) MONGO_URI = 'mongodb://localhost:27017/test-yith-library' class TestCase(unittest.TestCase):

    clean_collections = tuple()

    def setUp(self):
        settings = {
            'mongo_uri': MONGO_URI,
            'auth_tk_secret': '123456',
            'twitter_consumer_key': 'key',
            'twitter_consumer_secret': 'secret',
            'facebook_app_id': 'id',
            'facebook_app_secret': 'secret',
            'google_client_id': 'id',
            'google_client_secret': 'secret',
            'paypal_user': 'sdk-three_api1.sdk.com',
            'paypal_password': 'QFZCWN5HZM8VBG7Q',
            'paypal_signature': 'A-IzJhZZjhg29XQ2qnhapuwxIDzyAZQ92FRP5dqBzVesOkzbdUONzmOU',
            'testing': 'True',
            'pyramid_mailer.prefix': 'mail_',
            'mail_default_sender': 'no-reply@yithlibrary.com',
            'admin_emails': 'admin1@example.com admin2@example.com',
            'public_url_root': 'http://localhost:6543/',
            }
        app = main({}, **settings)
        self.testapp = TestApp(app)
        self.db = app.registry.settings['db_conn']['test-yith-library']

    def tearDown(self):
        for col in self.clean_collections:
            self.db.drop_collection(col)

        self.testapp.reset()

    def set_user_cookie(self, user_id):
        request = TestRequest.blank('', {})
        request.registry = self.testapp.app.registry
        remember_headers = remember(request, user_id)
        cookie_value = remember_headers[0][1].split('"')[1]
        self.testapp.cookies['auth_tkt'] = cookie_value

    def add_to_session(self, data):
        queryUtility = self.testapp.app.registry.queryUtility
        session_factory = queryUtility(ISessionFactory)
        request = DummyRequest()
        session = session_factory(request)
        for key, value in data.items():
            session[key] = value
        session.persist()
        self.testapp.cookies['beaker.session.id'] = session._sess.id

    def get_session(self, response):
        queryUtility = self.testapp.app.registry.queryUtility
        session_factory = queryUtility(ISessionFactory)
        request = response.request

        if not hasattr(request, 'add_response_callback'):
            request.add_response_callback = lambda r: r

        if 'Set-Cookie' in response.headers:
            request.environ['HTTP_COOKIE'] = response.headers['Set-Cookie']

        return session_factory(request)
import unittest
from pyramid import testing
from pyramid.testing import DummyRequest
from pyramid_mailer import get_mailer
from yithlibraryserver.db import MongoDB
from yithlibraryserver.user.accounts import get_available_providers
from yithlibraryserver.user.accounts import get_providers, get_n_passwords
from yithlibraryserver.user.accounts import get_accounts, merge_accounts
from yithlibraryserver.user.accounts import merge_users
from yithlibraryserver.user.accounts import notify_admins_of_account_removal
from yithlibraryserver.testing import MONGO_URI, TestCase
                    }, ''))

        self.db.users.update({'email': 'john@example.com'}, {
                '$set': {'email_verified': True},
                }, safe=True)
        self.assertEqual([{
                    'providers': [],
                    'is_current': False,
                    'passwords': 0,
                    'id': '',
                    'is_verified': False,
                    }, {
                    'providers': [{
                            'name': 'twitter',
                            'is_current': True,
                            }],
                    'passwords': 0,
                    'id': str(user_id),
                    'is_current': True,
                    'is_verified': True,
                    }], get_accounts(self.db, {
                    'email': 'john@example.com',
                    '_id': '',
                    }, 'twitter'))

        self.db.passwords.insert({'password': 'secret', 'owner': user_id},
                                 safe=True)
        self.db.users.update({'email': 'john@example.com'}, {
                '$set': {'twitter_id': 1234},
                }, safe=True)
        self.assertEqual([{
                    'providers': [],
                    'is_current': False,
                    'passwords': 0,
                    'id': '',
                    'is_verified': False,
                    }, {
                    'providers': [{
                            'name': 'twitter',
                            'is_current': False,
                            }],
                    'passwords': 1,
                    'id': str(user_id),
                    'is_current': False,
                    'is_verified': True,
                    }], get_accounts(self.db, {
                    'email': 'john@example.com',
                    '_id': '',
                    }, 'google'))

        self.db.users.update({'email': 'john@example.com'}, {
                '$set': {'google_id': 4321},
                }, safe=True)
        self.assertEqual([{
                    'providers': [],
                    'is_current': False,
                    'passwords': 0,
                    'id': '',
                    'is_verified': False,
                    }, {
                    'providers': [{
                            'name': 'google',
                            'is_current': True,
                            }, {
                            'name': 'twitter',
                            'is_current': False,
                            }],
                    'passwords': 1,
                    'id': str(user_id),
                    'is_current': True,
                    'is_verified': True,
                    }], get_accounts(self.db, {
                    'email': 'john@example.com',
                    '_id': '',
                    }, 'google'))

    def test_merge_accounts(self):
        self.assertEqual(0, merge_accounts(self.db, {}, []))

        master_id = self.db.users.insert({
                'email': 'john@example.com',
                'twitter_id': 1234,
                'authorized_apps': ['a', 'b'],
                }, safe=True)
        master_user = self.db.users.find_one({'_id': master_id}, safe=True)

        self.db.passwords.insert({
                'owner': master_id,
                'password1': 'secret1',
                }, safe=True)

        # merging with itself does nothing
        self.assertEqual(1, self.db.users.count())
        self.assertEqual(0, merge_accounts(self.db, master_user,
                                            [str(master_id)]))
        master_user_reloaded = self.db.users.find_one({'_id': master_id},
                                                      safe=True)
        self.assertEqual(master_user, master_user_reloaded)
        self.assertEqual(1, self.db.users.count())

        # merge with invented users does nothing neither
        self.assertEqual(0, merge_accounts(self.db, master_user,
                                           ['000000000000000000000000']))
        master_user_reloaded = self.db.users.find_one({'_id': master_id},
                                                      safe=True)
        self.assertEqual(master_user, master_user_reloaded)
        self.assertEqual(1, self.db.users.count())

        # let's create valid users
        other_id = self.db.users.insert({
                'email': 'john@example.com',
                'google_id': 4321,
                'authorized_apps': ['b', 'c'],
                }, safe=True)
        self.assertEqual(2, self.db.users.count())
        self.db.passwords.insert({
                'owner': other_id,
                'password2': 'secret2',
                }, safe=True)


        self.assertEqual(1, merge_accounts(self.db, master_user,