def _get_user_record(self, name, client=None): """Get the user's record.""" user = models.MongoDBUser(name) if user.is_ignored: 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
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
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 not user.is_ignored: users.append(user) LOG.debug('users = ' + str(users)) return guestagent_utils.serialize_list(users, limit=limit, marker=marker, include_marker=include_marker)
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.')
class GuestAgentMongoDBManagerTest(DatastoreManagerTest): @mock.patch.object(ImportOverrideStrategy, '_initialize_import_directory') def setUp(self, _): super(GuestAgentMongoDBManagerTest, self).setUp('mongodb') 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' self.host_wildcard = '%' # This is used in the test_*_user tests below self.serialized_user = { '_name': 'testdb.testuser', '_password': None, '_roles': [{ 'db': 'testdb', 'role': 'testrole' }], '_username': '******', '_databases': [], '_host': self.host_wildcard, '_database': { '_name': 'testdb', '_character_set': None, '_collate': None }, '_is_root': False } 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') @mock.patch.object(service, 'MongoDBClient') @mock.patch.object(service.MongoDBAdmin, '_admin_user') @mock.patch.object(service.MongoDBAdmin, '_get_user_record') def test_create_user(self, mocked_get_user, mocked_admin_user, mocked_client): user = self.serialized_user.copy() user['_password'] = '******' users = [user] client = mocked_client().__enter__()['testdb'] mocked_get_user.return_value = None 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.assertIsNone(next_marker) self.assertEqual(sorted([user1, user2], key=lambda x: x['_name']), users) @mock.patch.object(service.MongoDBAdmin, 'create_validated_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': self.host_wildcard, '_is_root': True } 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]) # FIXME(songjian):can not create database with null content, # so create a collection # db_client['dummy'].insert.assert_called_with({'dummy': True}) # db_client.drop_collection.assert_called_with('dummy') db_client.create_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 dbs, next_marker = self.manager.list_databases(self.context, limit=2, marker='db1', include_marker=True) mocked_list.assert_any_call() self.assertEqual([ models.MongoDBSchema('db1').serialize(), models.MongoDBSchema('db2').serialize() ], dbs) self.assertEqual('db2', 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')