Пример #1
0
 def _get_user_record(self, name, client=None):
     """Get the user's record."""
     user = models.MongoDBUser(name)
     if not self._is_modifiable_user(user.name):
         LOG.warning('Skipping retrieval of user with reserved '
                     'name %(user)s' % {'user': user.name})
         return None
     if client:
         user_info = client.admin.system.users.find_one({
             'user':
             user.username,
             'db':
             user.database.name
         })
     else:
         with MongoDBClient(self._admin_user()) as admin_client:
             user_info = admin_client.admin.system.users.find_one({
                 'user':
                 user.username,
                 'db':
                 user.database.name
             })
     if not user_info:
         return None
     user.roles = user_info['roles']
     return user
Пример #2
0
 def _admin_user(self):
     if not type(self).admin_user:
         creds = MongoDBCredentials()
         creds.read(system.MONGO_ADMIN_CREDS_FILE)
         user = models.MongoDBUser('admin.%s' % creds.username,
                                   creds.password)
         type(self).admin_user = user
     return type(self).admin_user
Пример #3
0
 def enable_root(self, password=None):
     """Create a user 'root' with role 'root'."""
     if not password:
         LOG.debug('Generating root user password.')
         password = utils.generate_random_password()
     root_user = models.MongoDBUser(name='admin.root', password=password)
     root_user.roles = {'db': 'admin', 'role': 'root'}
     self.create_validated_user(root_user)
     return root_user.serialize()
Пример #4
0
 def create_admin_user(self, password):
     """Create the admin user while the localhost exception is active."""
     LOG.debug('Creating the admin user.')
     creds = self.store_admin_password(password)
     user = models.MongoDBUser(name='admin.%s' % creds.username,
                               password=creds.password)
     user.roles = system.MONGO_ADMIN_ROLES
     with MongoDBClient(None) as client:
         MongoDBAdmin().create_user(user, client=client)
     LOG.debug('Created admin user.')
Пример #5
0
 def _get_user_record(self, name):
     """Get the user's record."""
     user = models.MongoDBUser(name)
     with MongoDBClient(self._admin_user()) as admin_client:
         user_info = admin_client.admin.system.users.find_one(
             {'user': user.username, 'db': user.database.name})
         if not user_info:
             return None
         user.roles = user_info['roles']
     return user
Пример #6
0
 def list_users(self, limit=None, marker=None, include_marker=False):
     """Get a list of all users."""
     users = []
     with MongoDBClient(self._admin_user()) as admin_client:
         for user_info in admin_client.admin.system.users.find():
             user = models.MongoDBUser(name=user_info['_id'])
             user.roles = user_info['roles']
             if self._is_modifiable_user(user.name):
                 users.append(user.serialize())
     LOG.debug('users = ' + str(users))
     return pagination.paginate_list(users, limit, marker, include_marker)
Пример #7
0
 def create_admin_user(self, password):
     """Create the admin user while the localhost exception is active."""
     LOG.debug('Creating the admin user.')
     creds = self.store_admin_password(password)
     user = models.MongoDBUser(name='admin.%s' % creds.username,
                               password=creds.password)
     user.roles = system.MONGO_ADMIN_ROLES
     # the driver engine is already cached, but we need to change it it
     with MongoDBClient(None, host='localhost',
                        port=MONGODB_PORT) as client:
         MongoDBAdmin().create_validated_user(user, client=client)
     # now revert to the normal engine
     self.status.set_host(host=netutils.get_my_ipv4(), port=MONGODB_PORT)
     LOG.debug('Created admin user.')
Пример #8
0
class GuestAgentMongoDBManagerTest(trove_testtools.TestCase):
    @mock.patch.object(service.MongoDBApp,
                       '_init_overrides_dir',
                       return_value='')
    def setUp(self, _):
        super(GuestAgentMongoDBManagerTest, self).setUp()
        self.context = trove_testtools.TroveTestContext(self)
        self.manager = manager.Manager()

        self.execute_with_timeout_patch = mock.patch.object(
            utils, 'execute_with_timeout', return_value=('0', ''))
        self.addCleanup(self.execute_with_timeout_patch.stop)
        self.execute_with_timeout_patch.start()

        self.pymongo_patch = mock.patch.object(pymongo, 'MongoClient')
        self.addCleanup(self.pymongo_patch.stop)
        self.pymongo_patch.start()

        self.mount_point = '/var/lib/mongodb'

    def tearDown(self):
        super(GuestAgentMongoDBManagerTest, self).tearDown()

    def test_update_status(self):
        self.manager.app.status = mock.MagicMock()
        self.manager.update_status(self.context)
        self.manager.app.status.update.assert_any_call()

    def _prepare_method(
        self,
        packages=['packages'],
        databases=None,
        memory_mb='2048',
        users=None,
        device_path=None,
        mount_point=None,
        backup_info=None,
        config_contents=None,
        root_password=None,
        overrides=None,
        cluster_config=None,
    ):
        """self.manager.app must be correctly mocked before calling."""

        self.manager.app.status = mock.Mock()

        self.manager.prepare(self.context,
                             packages,
                             databases,
                             memory_mb,
                             users,
                             device_path=device_path,
                             mount_point=mount_point,
                             backup_info=backup_info,
                             config_contents=config_contents,
                             root_password=root_password,
                             overrides=overrides,
                             cluster_config=cluster_config)

        self.manager.app.status.begin_install.assert_any_call()
        self.manager.app.install_if_needed.assert_called_with(packages)
        self.manager.app.stop_db.assert_any_call()
        self.manager.app.clear_storage.assert_any_call()

        (self.manager.app.apply_initial_guestagent_configuration.
         assert_called_once_with(cluster_config, self.mount_point))

    @mock.patch.object(volume, 'VolumeDevice')
    @mock.patch('os.path.exists')
    def test_prepare_for_volume(self, exists, mocked_volume):
        device_path = '/dev/vdb'

        self.manager.app = mock.Mock()

        self._prepare_method(device_path=device_path)

        mocked_volume().unmount_device.assert_called_with(device_path)
        mocked_volume().format.assert_any_call()
        mocked_volume().migrate_data.assert_called_with(self.mount_point)
        mocked_volume().mount.assert_called_with(self.mount_point)

    def test_secure(self):
        self.manager.app = mock.Mock()

        mock_secure = mock.Mock()
        self.manager.app.secure = mock_secure

        self._prepare_method()

        mock_secure.assert_called_with()

    @mock.patch.object(backup, 'restore')
    @mock.patch.object(service.MongoDBAdmin, 'is_root_enabled')
    def test_prepare_from_backup(self, mocked_root_check, mocked_restore):
        self.manager.app = mock.Mock()

        backup_info = {
            'id': 'backup_id_123abc',
            'location': 'fake-location',
            'type': 'MongoDBDump',
            'checksum': 'fake-checksum'
        }

        self._prepare_method(backup_info=backup_info)

        mocked_restore.assert_called_with(self.context, backup_info,
                                          '/var/lib/mongodb')
        mocked_root_check.assert_any_call()

    def test_prepare_with_databases(self):
        self.manager.app = mock.Mock()

        database = mock.Mock()
        mock_create_databases = mock.Mock()
        self.manager.create_database = mock_create_databases

        self._prepare_method(databases=[database])

        mock_create_databases.assert_called_with(self.context, [database])

    def test_prepare_with_users(self):
        self.manager.app = mock.Mock()

        user = mock.Mock()
        mock_create_users = mock.Mock()
        self.manager.create_user = mock_create_users

        self._prepare_method(users=[user])

        mock_create_users.assert_called_with(self.context, [user])

    @mock.patch.object(service.MongoDBAdmin, 'enable_root')
    def test_provide_root_password(self, mocked_enable_root):
        self.manager.app = mock.Mock()

        self._prepare_method(root_password='******')

        mocked_enable_root.assert_called_with('test_password')

    # This is used in the test_*_user tests below
    _serialized_user = {
        '_name': 'testdb.testuser',
        '_password': None,
        '_roles': [{
            'db': 'testdb',
            'role': 'testrole'
        }],
        '_username': '******',
        '_databases': [],
        '_host': None,
        '_database': {
            '_name': 'testdb',
            '_character_set': None,
            '_collate': None
        }
    }

    @mock.patch.object(service, 'MongoDBClient')
    @mock.patch.object(service.MongoDBAdmin, '_admin_user')
    def test_create_user(self, mocked_admin_user, mocked_client):
        user = self._serialized_user.copy()
        user['_password'] = '******'
        users = [user]

        client = mocked_client().__enter__()['testdb']

        self.manager.create_user(self.context, users)

        client.add_user.assert_called_with('testuser',
                                           password='******',
                                           roles=[{
                                               'db': 'testdb',
                                               'role': 'testrole'
                                           }])

    @mock.patch.object(service, 'MongoDBClient')
    @mock.patch.object(service.MongoDBAdmin, '_admin_user')
    def test_delete_user(self, mocked_admin_user, mocked_client):
        client = mocked_client().__enter__()['testdb']

        self.manager.delete_user(self.context, self._serialized_user)

        client.remove_user.assert_called_with('testuser')

    @mock.patch.object(service, 'MongoDBClient')
    @mock.patch.object(service.MongoDBAdmin, '_admin_user')
    def test_get_user(self, mocked_admin_user, mocked_client):
        mocked_find = mock.MagicMock(
            return_value={
                '_id': 'testdb.testuser',
                'user': '******',
                'db': 'testdb',
                'roles': [{
                    'db': 'testdb',
                    'role': 'testrole'
                }]
            })
        client = mocked_client().__enter__().admin
        client.system.users.find_one = mocked_find

        result = self.manager.get_user(self.context, 'testdb.testuser', None)

        mocked_find.assert_called_with({'user': '******', 'db': 'testdb'})
        self.assertEqual(self._serialized_user, result)

    @mock.patch.object(service, 'MongoDBClient')
    @mock.patch.object(service.MongoDBAdmin, '_admin_user')
    def test_list_users(self, mocked_admin_user, mocked_client):
        # roles are NOT returned by list_users
        user1 = self._serialized_user.copy()
        user2 = self._serialized_user.copy()
        user2['_name'] = 'testdb.otheruser'
        user2['_username'] = '******'
        user2['_roles'] = [{'db': 'testdb2', 'role': 'readWrite'}]
        user2['_databases'] = [{
            '_name': 'testdb2',
            '_character_set': None,
            '_collate': None
        }]

        mocked_find = mock.MagicMock(
            return_value=[{
                '_id': 'admin.os_admin',
                'user': '******',
                'db': 'admin',
                'roles': [{
                    'db': 'admin',
                    'role': 'root'
                }]
            }, {
                '_id': 'testdb.testuser',
                'user': '******',
                'db': 'testdb',
                'roles': [{
                    'db': 'testdb',
                    'role': 'testrole'
                }]
            }, {
                '_id': 'testdb.otheruser',
                'user': '******',
                'db': 'testdb',
                'roles': [{
                    'db': 'testdb2',
                    'role': 'readWrite'
                }]
            }])

        client = mocked_client().__enter__().admin
        client.system.users.find = mocked_find

        users, next_marker = self.manager.list_users(self.context)

        self.assertEqual(None, next_marker)
        self.assertEqual(sorted([user1, user2]), users)

    @mock.patch.object(service.MongoDBAdmin, 'create_user')
    @mock.patch.object(utils,
                       'generate_random_password',
                       return_value='password')
    def test_enable_root(self, mock_gen_rand_pwd, mock_create_user):
        root_user = {
            '_name': 'admin.root',
            '_username': '******',
            '_database': {
                '_name': 'admin',
                '_character_set': None,
                '_collate': None
            },
            '_password': '******',
            '_roles': [{
                'db': 'admin',
                'role': 'root'
            }],
            '_databases': [],
            '_host': None
        }

        result = self.manager.enable_root(self.context)

        self.assertTrue(mock_create_user.called)
        self.assertEqual(root_user, result)

    @mock.patch.object(service, 'MongoDBClient')
    @mock.patch.object(service.MongoDBAdmin, '_admin_user')
    @mock.patch.object(service.MongoDBAdmin,
                       '_get_user_record',
                       return_value=models.MongoDBUser('testdb.testuser'))
    def test_grant_access(self, mocked_get_user, mocked_admin_user,
                          mocked_client):
        client = mocked_client().__enter__()['testdb']

        self.manager.grant_access(self.context, 'testdb.testuser', None,
                                  ['db1', 'db2', 'db3'])

        client.add_user.assert_called_with('testuser',
                                           roles=[{
                                               'db': 'db1',
                                               'role': 'readWrite'
                                           }, {
                                               'db': 'db2',
                                               'role': 'readWrite'
                                           }, {
                                               'db': 'db3',
                                               'role': 'readWrite'
                                           }])

    @mock.patch.object(service, 'MongoDBClient')
    @mock.patch.object(service.MongoDBAdmin, '_admin_user')
    @mock.patch.object(service.MongoDBAdmin,
                       '_get_user_record',
                       return_value=models.MongoDBUser('testdb.testuser'))
    def test_revoke_access(self, mocked_get_user, mocked_admin_user,
                           mocked_client):
        client = mocked_client().__enter__()['testdb']

        mocked_get_user.return_value.roles = [{
            'db': 'db1',
            'role': 'readWrite'
        }, {
            'db': 'db2',
            'role': 'readWrite'
        }, {
            'db': 'db3',
            'role': 'readWrite'
        }]

        self.manager.revoke_access(self.context, 'testdb.testuser', None,
                                   'db2')

        client.add_user.assert_called_with('testuser',
                                           roles=[{
                                               'db': 'db1',
                                               'role': 'readWrite'
                                           }, {
                                               'db': 'db3',
                                               'role': 'readWrite'
                                           }])

    @mock.patch.object(service, 'MongoDBClient')
    @mock.patch.object(service.MongoDBAdmin, '_admin_user')
    @mock.patch.object(service.MongoDBAdmin,
                       '_get_user_record',
                       return_value=models.MongoDBUser('testdb.testuser'))
    def test_list_access(self, mocked_get_user, mocked_admin_user,
                         mocked_client):
        mocked_get_user.return_value.roles = [{
            'db': 'db1',
            'role': 'readWrite'
        }, {
            'db': 'db2',
            'role': 'readWrite'
        }, {
            'db': 'db3',
            'role': 'readWrite'
        }]

        accessible_databases = self.manager.list_access(
            self.context, 'testdb.testuser', None)

        self.assertEqual(['db1', 'db2', 'db3'],
                         [db['_name'] for db in accessible_databases])

    @mock.patch.object(service, 'MongoDBClient')
    @mock.patch.object(service.MongoDBAdmin, '_admin_user')
    def test_create_databases(self, mocked_admin_user, mocked_client):
        schema = models.MongoDBSchema('testdb').serialize()
        db_client = mocked_client().__enter__()['testdb']

        self.manager.create_database(self.context, [schema])

        db_client['dummy'].insert.assert_called_with({'dummy': True})
        db_client.drop_collection.assert_called_with('dummy')

    @mock.patch.object(service, 'MongoDBClient')
    @mock.patch.object(service.MongoDBAdmin, '_admin_user')
    def test_list_databases(
            self,  # mocked_ignored_dbs,
            mocked_admin_user,
            mocked_client):
        # This list contains the special 'admin', 'local' and 'config' dbs;
        # the special dbs should be skipped in the output.
        # Pagination is tested by starting at 'db1', so 'db0' should not
        # be in the output. The limit is set to 2, meaning the result
        # should be 'db1' and 'db2'. The next_marker should be 'db3'.
        mocked_list = mock.MagicMock(return_value=[
            'admin', 'local', 'config', 'db0', 'db1', 'db2', 'db3'
        ])
        mocked_client().__enter__().database_names = mocked_list

        marker = models.MongoDBSchema('db1').serialize()
        dbs, next_marker = self.manager.list_databases(self.context,
                                                       limit=2,
                                                       marker=marker,
                                                       include_marker=True)

        mocked_list.assert_any_call()
        self.assertEqual([
            models.MongoDBSchema('db1').serialize(),
            models.MongoDBSchema('db2').serialize()
        ], dbs)
        self.assertEqual(models.MongoDBSchema('db2').serialize(), next_marker)

    @mock.patch.object(service, 'MongoDBClient')
    @mock.patch.object(service.MongoDBAdmin, '_admin_user')
    def test_delete_database(self, mocked_admin_user, mocked_client):
        schema = models.MongoDBSchema('testdb').serialize()

        self.manager.delete_database(self.context, schema)

        mocked_client().__enter__().drop_database.assert_called_with('testdb')