def test_recreate_job_as_project_member(self): from pillar.api.projects.utils import get_admin_group_id import pillar.auth from flamenco.jobs import routes with self.app.test_request_context(): groups = self.create_standard_groups() admin_gid = get_admin_group_id(self.proj_id) self.create_user(24 * 'd', roles={'subscriber'}, groups=[groups['subscriber'], admin_gid], token='user-token') old_task_ids = self._pre_test() # Test we're able to get the job itself. self.get(f'/api/flamenco/jobs/{self.job_id}', auth_token='user-token') # The user should also be allowed to recreate the job. with self.app.test_request_context(): pillar.auth.login_user('user-token', load_from_db=True) routes.recreate_job(self.project['url'], self.job_id) self._post_test(old_task_ids)
def test_set_job_valid_status_as_projmember_subscriber( self, mock_handle_job_status_change): """Subscribers member of the project should be allowed to do this.""" from pillar.api.projects.utils import get_admin_group_id with self.app.test_request_context(): admin_group_id = get_admin_group_id(self.proj_id) self.create_user(user_id=24 * 'e', roles={'subscriber'}, groups=[admin_group_id], token='flamuser-token') self.assert_job_status('queued') mock_handle_job_status_change.return_value = None self.patch( '/api/flamenco/jobs/%s' % self.job_id, json={ 'op': 'set-job-status', 'status': 'completed' }, auth_token='flamuser-token', expected_status=204, ) mock_handle_job_status_change.assert_called_with( self.job_id, 'queued', 'completed') self.assert_job_status('completed')
def setUp(self, **kwargs): AbstractFlamencoTest.setUp(self, **kwargs) from pillar.api.utils.authentication import force_cli_user from pillar.api.projects.utils import get_admin_group_id mngr_doc, account, token = self.create_manager_service_account() self.mngr_id = mngr_doc['_id'] self.mngr_token = token['token'] self.assign_manager_to_project(self.mngr_id, self.proj_id) with self.app.test_request_context(): group_id = get_admin_group_id(self.proj_id) self.create_user(user_id=24 * 'f', roles={'flamenco-admin'}, groups=[group_id]) self.create_valid_auth_token(24 * 'f', 'fladmin-token') with self.app.test_request_context(): force_cli_user() job = self.jmngr.api_create_job( 'test job', 'Wörk wørk w°rk.', 'sleep', { 'frames': '12-18, 20-22', 'chunk_size': 3, 'time_in_seconds': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, ) self.job_id = job['_id']
def setUp(self, **kwargs): AbstractFlamencoTest.setUp(self, **kwargs) from pillar.api.utils.authentication import force_cli_user from pillar.api.projects.utils import get_admin_group_id mngr_doc, account, token = self.create_manager_service_account() self.mngr_id = mngr_doc['_id'] self.mngr_token = token['token'] self.assign_manager_to_project(self.mngr_id, self.proj_id) with self.app.test_request_context(): group_id = get_admin_group_id(self.proj_id) self.create_user(user_id=24 * 'f', roles={'flamenco-admin'}, groups=[group_id]) self.create_valid_auth_token(24 * 'f', 'fladmin-token') with self.app.test_request_context(): force_cli_user() job = self.jmngr.api_create_job( 'test job', 'Wörk wørk w°rk.', 'sleep', { 'frames': '12-18, 20-22', 'chunk_size': 3, 'time_in_seconds': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, ) self.job_id = job['_id']
def test_set_job_valid_status_as_projmember_subscriber(self, mock_handle_job_status_change): """Subscribers member of the project should be allowed to do this.""" from pillar.api.projects.utils import get_admin_group_id with self.app.test_request_context(): admin_group_id = get_admin_group_id(self.proj_id) self.create_user(user_id=24 * 'e', roles={'subscriber'}, groups=[admin_group_id], token='flamuser-token') self.assert_job_status('queued') mock_handle_job_status_change.return_value = None self.patch( '/api/flamenco/jobs/%s' % self.job_id, json={'op': 'set-job-status', 'status': 'completed'}, auth_token='flamuser-token', expected_status=204, ) mock_handle_job_status_change.assert_called_with( self.job_id, 'queued', 'completed') self.assert_job_status('completed')
def setUp(self, **kwargs): AbstractFlamencoTest.setUp(self, **kwargs) from pillar.api.utils.authentication import force_cli_user from pillar.api.projects.utils import get_admin_group_id mngr_doc, account, token = self.create_manager_service_account() self.mngr_id = mngr_doc['_id'] self.mngr_token = token['token'] self.assign_manager_to_project(self.mngr_id, self.proj_id) with self.app.test_request_context(): group_id = get_admin_group_id(self.proj_id) self.create_user(user_id=24 * 'f', roles={'flamenco-admin'}, groups=[group_id]) self.create_valid_auth_token(24 * 'f', 'fladmin-token') with self.app.test_request_context(): force_cli_user() job = self.jmngr.api_create_job( 'test job', 'Wörk wørk w°rk.', 'blender-render-progressive', { 'frames': '1-5', 'chunk_size': 2, 'render_output': '/render/out/frames-######', 'format': 'EXR', 'filepath': '/agent327/scenes/someshot/somefile.blend', 'blender_cmd': '/path/to/blender --enable-new-depsgraph', 'cycles_sample_count': 30, 'cycles_num_chunks': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, ) self.job_id = job['_id']
def setUp(self, **kwargs): AbstractFlamencoTest.setUp(self, **kwargs) from pillar.api.utils.authentication import force_cli_user from pillar.api.projects.utils import get_admin_group_id mngr_doc, account, token = self.create_manager_service_account() self.mngr_id = mngr_doc['_id'] self.mngr_token = token['token'] self.assign_manager_to_project(self.mngr_id, self.proj_id) with self.app.test_request_context(): group_id = get_admin_group_id(self.proj_id) self.create_user(user_id=24 * 'f', roles={'flamenco-admin'}, groups=[group_id]) self.create_valid_auth_token(24 * 'f', 'fladmin-token') with self.app.test_request_context(): force_cli_user() job = self.jmngr.api_create_job( 'test job', 'Wörk wørk w°rk.', 'blender-render-progressive', { 'frames': '1-5', 'chunk_size': 2, 'render_output': '/render/out/frames-######', 'format': 'OPEN_EXR', 'fps': 44, 'filepath': '/agent327/scenes/someshot/somefile.blend', 'blender_cmd': '/path/to/blender --enable-new-depsgraph', 'cycles_sample_count': 30, 'cycles_sample_cap': 30, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, ) self.job_id = job['_id']
def api_assign_to_project(self, manager_id: bson.ObjectId, project_id: bson.ObjectId, action: str) -> bool: """Assigns the manager to the given project. Does NOT check whether the project actually exists or not. :param action: either 'assign' or 'remove' :returns: True iff the action was successful. """ from collections import defaultdict from pymongo.results import UpdateResult from flamenco import current_flamenco from pillar.api.projects import utils as project_utils if action not in {'assign', 'remove'}: raise ValueError("Action must be either 'assign' or 'remove'") assert isinstance(manager_id, bson.ObjectId) assert isinstance(project_id, bson.ObjectId) mngr_coll = current_flamenco.db('managers') manager_doc = mngr_coll.find_one({'_id': manager_id}, { 'projects': 1, 'user_groups': 1 }) if not manager_doc: self._log.warning( 'api_assign_to_project(%s, %s): no manager with id=%s (user=%s)', manager_id, project_id, manager_id, current_user.user_id) return False mngr_projects = set(manager_doc.get('projects', [])) mngr_user_groups = set(manager_doc.get('user_groups', [])) admin_group_id = project_utils.get_admin_group_id(project_id) if action == 'assign': mngr_projects.add(project_id) mngr_user_groups.add(admin_group_id) else: mngr_projects.discard(project_id) mngr_user_groups.discard(admin_group_id) # Convert to list because JSON/BSON doesn't do sets, and sort to get predictable output. projects = sorted(mngr_projects) user_groups = sorted(mngr_user_groups) if self._log.isEnabledFor(logging.INFO): self._log.info( 'Updating Manager %s projects to [%s] and user_groups to [%s]', manager_id, ', '.join(f"'{pid}'" for pid in projects), ', '.join(f"'{gid}'" for gid in user_groups), ) update = defaultdict(dict) # type: typing.DefaultDict[str, typing.Any] if projects: update['$set']['projects'] = projects else: update['$unset']['projects'] = 1 if user_groups: update['$set']['user_groups'] = user_groups else: update['$unset']['user_groups'] = 1 res: UpdateResult = mngr_coll.update_one({'_id': manager_id}, update) if res.matched_count < 1: self._log.error( 'Unable to update projects on Manager %s to %s: %s', manager_id, ', '.join(f"'{pid}'" for pid in projects), res) return False return True
def setUp(self, **kwargs): super().setUp(**kwargs) # Create multiple projects: # 1) user is member, owned manager assigned to it. # 2) user is member, non-owned manager assigned to it. # 3) user is member, no manager assigned to it. # 4) user is not member, both non-owned and owned manager assigned to it. # 5) user is not member, only non-owned manager assigned to it. # 6) user is GET-only member, owned manager assigned to it. # 7) user is GET-only member, non-owned manager assigned to it. from pillar.api.projects.utils import get_admin_group_id from pillar.api.utils import remove_private_keys # Create the projects self.project: typing.MutableMapping[int, dict] = {} self.prid: typing.MutableMapping[int, bson.ObjectId] = {} for idx in range(1, 8): admin_id = 24 * str(idx) proj = self._create_user_and_project( user_id=admin_id, roles={'subscriber'}, project_name=f'Prøject {idx}', token=f'token-proj{idx}-admin') self.project[idx] = proj self.prid[idx] = bson.ObjectId(proj['_id']) # For projects 6 and 7, add GET access group self.group_map = self.create_standard_groups( additional_groups=['get-only-6', 'get-only-7']) for idx in (6, 7): self.project[idx]['permissions']['groups'].append({ 'group': self.group_map[f'get-only-{idx}'], 'methods': ['GET'], }) self.put(f'/api/projects/{self.prid[idx]}', json=remove_private_keys(self.project[idx]), etag=self.project[idx]['_etag'], auth_token=f'token-proj{idx}-admin') # Create the managers self.owned_mngr, _, self.owned_mngr_token = self.create_manager_service_account( ) self.owned_mngr_id = bson.ObjectId(self.owned_mngr['_id']) self.nonowned_mngr, _, self.nonowned_mngr_token = self.create_manager_service_account( ) self.nonowned_mngr_id = bson.ObjectId(self.nonowned_mngr['_id']) self.assign_manager_to_project(self.owned_mngr_id, self.prid[1]) self.assign_manager_to_project(self.nonowned_mngr_id, self.prid[2]) self.assign_manager_to_project(self.owned_mngr_id, self.prid[4]) self.assign_manager_to_project(self.nonowned_mngr_id, self.prid[5]) self.assign_manager_to_project(self.owned_mngr_id, self.prid[6]) self.assign_manager_to_project(self.nonowned_mngr_id, self.prid[7]) # Create the test user. self.admin_gid: typing.MutableMapping[int, bson.ObjectId] = {} with self.app.test_request_context(): for idx, prid in self.prid.items(): self.admin_gid[idx] = get_admin_group_id(prid) self.create_user(roles={'subscriber', 'flamenco-user'}, groups=[ self.admin_gid[1], self.admin_gid[2], self.admin_gid[3], self.owned_mngr['owner'], self.group_map['get-only-6'], self.group_map['get-only-7'], ], token='user-token') # Make some assertions about the access rights on the projects. for idx in (1, 2, 3): p = self.get(f'/api/projects/{self.prid[idx]}', auth_token='user-token').json() self.assertEqual({ 'GET', 'PUT', 'DELETE', 'POST' }, set( p['allowed_methods'] ), f'Unexpected methods {p["allowed_methods"]} in project nr {idx}' ) for idx in (4, 5): self.get(f'/api/projects/{self.prid[idx]}', auth_token='user-token', expected_status=403) for idx in (6, 7): p = self.get(f'/api/projects/{self.prid[idx]}', auth_token='user-token').json() self.assertEqual({'GET'}, set( p['allowed_methods'] ), f'Unexpected methods {p["allowed_methods"]} in project nr {idx}' )
def setUp(self, **kwargs): AbstractFlamencoTest.setUp(self, **kwargs) from pillar.api.utils.authentication import force_cli_user mngr_doc, account, token = self.create_manager_service_account( assign_to_project_id=self.proj_id) self.mngr_id = mngr_doc['_id'] self.mngr_token = token['token'] with self.app.app_context(): project_gid = get_admin_group_id(self.proj_id) self.user = self.create_user(roles={'subscriber'}, groups=[project_gid, mngr_doc['owner']], token='user-token') # Create three test jobs, one of which is completed and two are queued. with self.app.test_request_context(): force_cli_user() job = self.jmngr.api_create_job( 'test job 1', 'Wörk wørk w°rk.', 'sleep', { 'frames': '12-18, 20-22', 'chunk_size': 3, 'time_in_seconds': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, ) self.jobid1 = job['_id'] job = self.jmngr.api_create_job( 'test job 2', 'Wörk wørk w°rk.', 'sleep', { 'frames': '12-18, 20-22', 'chunk_size': 3, 'time_in_seconds': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, ) self.jobid2 = job['_id'] job = self.jmngr.api_create_job( 'test job 3', 'Wörk wørk w°rk.', 'sleep', { 'frames': '12-18, 20-22', 'chunk_size': 3, 'time_in_seconds': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, ) self.jobid3 = job['_id'] job = self.jmngr.api_create_job( 'test job 4', 'Wörk wørk w°rk.', 'sleep', { 'frames': '12-18, 20-22', 'chunk_size': 3, 'time_in_seconds': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, start_paused=True, ) self.jobid4 = job['_id'] assert isinstance(self.jobid1, ObjectId) assert isinstance(self.jobid2, ObjectId) assert isinstance(self.jobid3, ObjectId) assert isinstance(self.jobid4, ObjectId) self.set_job_status('completed', job_id=self.jobid3) self.tasks = list(self.flamenco.db('tasks').find({ 'job': {'$in': [self.jobid1, self.jobid2]} })) self.task_ids = [t['_id'] for t in self.tasks]
def setUp(self, **kwargs): AbstractFlamencoTest.setUp(self, **kwargs) from pillar.api.utils.authentication import force_cli_user mngr_doc, account, token = self.create_manager_service_account( assign_to_project_id=self.proj_id) self.mngr_id = mngr_doc['_id'] self.mngr_token = token['token'] with self.app.app_context(): project_gid = get_admin_group_id(self.proj_id) self.user = self.create_user(roles={'subscriber'}, groups=[project_gid, mngr_doc['owner']], token='user-token') # Create three test jobs, one of which is completed and two are queued. with self.app.test_request_context(): force_cli_user() job = self.jmngr.api_create_job( 'test job 1', 'Wörk wørk w°rk.', 'sleep', { 'frames': '12-18, 20-22', 'chunk_size': 3, 'time_in_seconds': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, ) self.jobid1 = job['_id'] job = self.jmngr.api_create_job( 'test job 2', 'Wörk wørk w°rk.', 'sleep', { 'frames': '12-18, 20-22', 'chunk_size': 3, 'time_in_seconds': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, ) self.jobid2 = job['_id'] job = self.jmngr.api_create_job( 'test job 3', 'Wörk wørk w°rk.', 'sleep', { 'frames': '12-18, 20-22', 'chunk_size': 3, 'time_in_seconds': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, ) self.jobid3 = job['_id'] job = self.jmngr.api_create_job( 'test job 4', 'Wörk wørk w°rk.', 'sleep', { 'frames': '12-18, 20-22', 'chunk_size': 3, 'time_in_seconds': 3, }, self.proj_id, ctd.EXAMPLE_PROJECT_OWNER_ID, self.mngr_id, start_paused=True, ) self.jobid4 = job['_id'] assert isinstance(self.jobid1, ObjectId) assert isinstance(self.jobid2, ObjectId) assert isinstance(self.jobid3, ObjectId) assert isinstance(self.jobid4, ObjectId) self.set_job_status('completed', job_id=self.jobid3) self.tasks = list( self.flamenco.db('tasks').find( {'job': { '$in': [self.jobid1, self.jobid2] }})) self.task_ids = [t['_id'] for t in self.tasks]
def setUp(self, **kwargs): super().setUp(**kwargs) # Create multiple projects: # 1) user is member, owned manager assigned to it. # 2) user is member, non-owned manager assigned to it. # 3) user is member, no manager assigned to it. # 4) user is not member, both non-owned and owned manager assigned to it. # 5) user is not member, only non-owned manager assigned to it. # 6) user is GET-only member, owned manager assigned to it. # 7) user is GET-only member, non-owned manager assigned to it. from pillar.api.projects.utils import get_admin_group_id from pillar.api.utils import remove_private_keys # Create the projects self.project: typing.MutableMapping[int, dict] = {} self.prid: typing.MutableMapping[int, bson.ObjectId] = {} for idx in range(1, 8): admin_id = 24 * str(idx) proj = self._create_user_and_project(user_id=admin_id, roles={'subscriber'}, project_name=f'Prøject {idx}', token=f'token-proj{idx}-admin') self.project[idx] = proj self.prid[idx] = bson.ObjectId(proj['_id']) # For projects 6 and 7, add GET access group self.group_map = self.create_standard_groups(additional_groups=['get-only-6', 'get-only-7']) for idx in (6, 7): self.project[idx]['permissions']['groups'].append({ 'group': self.group_map[f'get-only-{idx}'], 'methods': ['GET'], }) self.put(f'/api/projects/{self.prid[idx]}', json=remove_private_keys(self.project[idx]), etag=self.project[idx]['_etag'], auth_token=f'token-proj{idx}-admin') # Create the managers self.owned_mngr, _, self.owned_mngr_token = self.create_manager_service_account() self.owned_mngr_id = bson.ObjectId(self.owned_mngr['_id']) self.nonowned_mngr, _, self.nonowned_mngr_token = self.create_manager_service_account() self.nonowned_mngr_id = bson.ObjectId(self.nonowned_mngr['_id']) self.assign_manager_to_project(self.owned_mngr_id, self.prid[1]) self.assign_manager_to_project(self.nonowned_mngr_id, self.prid[2]) self.assign_manager_to_project(self.owned_mngr_id, self.prid[4]) self.assign_manager_to_project(self.nonowned_mngr_id, self.prid[5]) self.assign_manager_to_project(self.owned_mngr_id, self.prid[6]) self.assign_manager_to_project(self.nonowned_mngr_id, self.prid[7]) # Create the test user. self.admin_gid: typing.MutableMapping[int, bson.ObjectId] = {} with self.app.test_request_context(): for idx, prid in self.prid.items(): self.admin_gid[idx] = get_admin_group_id(prid) self.create_user( roles={'subscriber'}, groups=[ self.admin_gid[1], self.admin_gid[2], self.admin_gid[3], self.owned_mngr['owner'], self.group_map['get-only-6'], self.group_map['get-only-7'], ], token='user-token') # Make some assertions about the access rights on the projects. for idx in (1, 2, 3): p = self.get(f'/api/projects/{self.prid[idx]}', auth_token='user-token').json self.assertEqual({'GET', 'PUT', 'DELETE', 'POST'}, set(p['allowed_methods']), f'Unexpected methods {p["allowed_methods"]} in project nr {idx}') for idx in (4, 5): self.get(f'/api/projects/{self.prid[idx]}', auth_token='user-token', expected_status=403) for idx in (6, 7): p = self.get(f'/api/projects/{self.prid[idx]}', auth_token='user-token').json self.assertEqual({'GET'}, set(p['allowed_methods']), f'Unexpected methods {p["allowed_methods"]} in project nr {idx}')