diff --git a/jobbergate-api/CHANGELOG.md b/jobbergate-api/CHANGELOG.md index 2104f710d..7e6fff113 100644 --- a/jobbergate-api/CHANGELOG.md +++ b/jobbergate-api/CHANGELOG.md @@ -4,6 +4,8 @@ This file keeps track of all notable changes to jobbergate-api ## Unreleased +- Expanded permission sets from view/edit to create/read/update/delete + ## 5.0.0a0 -- 2024-03-26 - Fixed a bug when an empty string is passed as a value for `execution_directory` on job submissions diff --git a/jobbergate-api/jobbergate_api/apps/job_script_templates/routers.py b/jobbergate-api/jobbergate_api/apps/job_script_templates/routers.py index 2db0905c8..52d756045 100644 --- a/jobbergate-api/jobbergate_api/apps/job_script_templates/routers.py +++ b/jobbergate-api/jobbergate_api/apps/job_script_templates/routers.py @@ -39,7 +39,7 @@ async def job_script_template_create( create_request: JobTemplateCreateRequest, secure_services: SecureService = Depends( - secure_services(Permissions.JOB_TEMPLATES_EDIT, ensure_email=True) + secure_services(Permissions.JOB_TEMPLATES_CREATE, ensure_email=True) ), ): """Create a new job script template.""" @@ -63,7 +63,7 @@ async def job_script_template_create( ) async def job_script_template_get( id_or_identifier: int | str = Path(), - secure_services: SecureService = Depends(secure_services(Permissions.JOB_TEMPLATES_VIEW, commit=False)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_TEMPLATES_READ, commit=False)), ): """Get a job script template by id or identifier.""" logger.info(f"Getting job script template with {id_or_identifier=}") @@ -80,7 +80,7 @@ async def job_script_template_clone( id_or_identifier: int | str = Path(), clone_request: JobTemplateCloneRequest | None = None, secure_services: SecureService = Depends( - secure_services(Permissions.JOB_TEMPLATES_EDIT, ensure_email=True) + secure_services(Permissions.JOB_TEMPLATES_CREATE, ensure_email=True) ), ): """Clone a job script template by id or identifier.""" @@ -123,7 +123,7 @@ async def job_script_template_clone( async def job_script_template_get_list( list_params: ListParams = Depends(), include_null_identifier: bool = Query(False), - secure_services: SecureService = Depends(secure_services(Permissions.JOB_TEMPLATES_VIEW, commit=False)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_TEMPLATES_READ, commit=False)), ): """Get a list of job script templates.""" logger.debug("Preparing to list job script templates") @@ -149,7 +149,7 @@ async def job_script_template_update( update_request: JobTemplateUpdateRequest, id_or_identifier: int | str = Path(), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_TEMPLATES_EDIT, ensure_email=True) + secure_services(Permissions.JOB_TEMPLATES_UPDATE, ensure_email=True) ), ): """Update a job script template by id or identifier.""" @@ -170,7 +170,7 @@ async def job_script_template_update( async def job_script_template_delete( id_or_identifier: int | str = Path(), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_TEMPLATES_EDIT, ensure_email=True) + secure_services(Permissions.JOB_TEMPLATES_DELETE, ensure_email=True) ), ): """Delete a job script template by id or identifier.""" @@ -189,7 +189,7 @@ async def job_script_template_delete( async def job_script_template_get_file( id_or_identifier: int | str = Path(), file_name: str = Path(), - secure_services: SecureService = Depends(secure_services(Permissions.JOB_TEMPLATES_VIEW, commit=False)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_TEMPLATES_READ, commit=False)), ): """ Get a job script template file by id or identifier. @@ -218,7 +218,7 @@ async def job_script_template_upload_file( file_type: FileType = Path(), upload_file: UploadFile = File(..., description="File to upload"), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_TEMPLATES_EDIT, ensure_email=True) + secure_services(Permissions.JOB_TEMPLATES_CREATE, ensure_email=True) ), ): """Upload a file to a job script template by id or identifier.""" @@ -250,7 +250,7 @@ async def job_script_template_delete_file( id_or_identifier: int | str = Path(), file_name: str = Path(), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_TEMPLATES_EDIT, ensure_email=True) + secure_services(Permissions.JOB_TEMPLATES_DELETE, ensure_email=True) ), ): """Delete a file from a job script template by id or identifier.""" @@ -267,7 +267,7 @@ async def job_script_template_delete_file( ) async def job_script_workflow_get_file( id_or_identifier: int | str = Path(), - secure_services: SecureService = Depends(secure_services(Permissions.JOB_TEMPLATES_VIEW, commit=False)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_TEMPLATES_READ, commit=False)), ): """ Get a workflow file by id or identifier. @@ -298,7 +298,7 @@ async def job_script_workflow_upload_file( ), upload_file: UploadFile = File(..., description="File to upload"), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_TEMPLATES_EDIT, ensure_email=True) + secure_services(Permissions.JOB_TEMPLATES_CREATE, ensure_email=True) ), ): """Upload a file to a job script workflow by id or identifier.""" @@ -336,7 +336,7 @@ async def job_script_workflow_upload_file( async def job_script_workflow_delete_file( id_or_identifier: int | str = Path(), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_TEMPLATES_EDIT, ensure_email=True) + secure_services(Permissions.JOB_TEMPLATES_DELETE, ensure_email=True) ), ): """Delete a workflow file from a job script template by id or identifier.""" @@ -356,7 +356,7 @@ async def job_script_workflow_delete_file( ) async def job_script_template_garbage_collector( background_tasks: BackgroundTasks, - secure_services: SecureService = Depends(secure_services(Permissions.JOB_TEMPLATES_EDIT)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_TEMPLATES_DELETE)), ): """Delete all unused files from jobbergate templates on the file storage.""" logger.info("Starting garbage collection from jobbergate file storage") diff --git a/jobbergate-api/jobbergate_api/apps/job_scripts/routers.py b/jobbergate-api/jobbergate_api/apps/job_scripts/routers.py index d136553dc..c25bf3c09 100644 --- a/jobbergate-api/jobbergate_api/apps/job_scripts/routers.py +++ b/jobbergate-api/jobbergate_api/apps/job_scripts/routers.py @@ -39,7 +39,7 @@ ) def job_script_auto_clean_unused_entries( background_tasks: BackgroundTasks, - secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_EDIT)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_DELETE)), ): """Automatically clean unused job scripts depending on a threshold.""" logger.info("Starting automatically cleanup for unused job scripts") @@ -55,7 +55,7 @@ def job_script_auto_clean_unused_entries( ) async def job_script_create( create_request: JobScriptCreateRequest, - secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_EDIT)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_CREATE)), ): """Create a stand alone job script.""" logger.info(f"Creating a new job script with {create_request=}") @@ -82,7 +82,7 @@ async def job_script_clone( id: int = Path(), clone_request: JobScriptCloneRequest | None = None, secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SCRIPTS_EDIT, ensure_email=True) + secure_services(Permissions.JOB_SCRIPTS_CREATE, ensure_email=True) ), ): """Clone a job script by its id.""" @@ -116,7 +116,7 @@ async def job_script_create_from_template( render_request: RenderFromTemplateRequest, id_or_identifier: int | str = Path(...), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SCRIPTS_EDIT, ensure_email=True) + secure_services(Permissions.JOB_SCRIPTS_CREATE, ensure_email=True) ), ): """Create a new job script from a job script template.""" @@ -189,7 +189,7 @@ async def job_script_create_from_template( ) async def job_script_get( id: int = Path(), - secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_VIEW, commit=False)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_READ, commit=False)), ): """Get a job script by id.""" logger.info(f"Getting job script {id=}") @@ -207,7 +207,7 @@ async def job_script_get_list( None, description="Filter job-scripts by the job-script-template-id they were created from.", ), - secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_VIEW, commit=False)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_READ, commit=False)), ): """Get a list of job scripts.""" logger.debug("Preparing to list job scripts") @@ -232,7 +232,7 @@ async def job_script_update( update_params: JobScriptUpdateRequest, id: int = Path(), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SCRIPTS_EDIT, ensure_email=True) + secure_services(Permissions.JOB_SCRIPTS_UPDATE, ensure_email=True) ), ): """Update a job script template by id or identifier.""" @@ -251,7 +251,7 @@ async def job_script_update( async def job_script_delete( id: int = Path(...), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SCRIPTS_EDIT, ensure_email=True) + secure_services(Permissions.JOB_SCRIPTS_DELETE, ensure_email=True) ), ): """Delete a job script template by id or identifier.""" @@ -270,7 +270,7 @@ async def job_script_delete( async def job_script_get_file( id: int = Path(...), file_name: str = Path(...), - secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_VIEW, commit=False)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_READ, commit=False)), ): """ Get a job script file. @@ -297,7 +297,7 @@ async def job_script_upload_file( file_type: FileType = Path(...), upload_file: UploadFile = File(..., description="File to upload"), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SCRIPTS_EDIT, ensure_email=True) + secure_services(Permissions.JOB_SCRIPTS_CREATE, ensure_email=True) ), ): """Upload a file to a job script.""" @@ -330,7 +330,7 @@ async def job_script_delete_file( id: int = Path(...), file_name: str = Path(...), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SCRIPTS_EDIT, ensure_email=True) + secure_services(Permissions.JOB_SCRIPTS_DELETE, ensure_email=True) ), ): """Delete a file from a job script template by id or identifier.""" @@ -349,7 +349,7 @@ async def job_script_delete_file( ) def job_script_garbage_collector( background_tasks: BackgroundTasks, - secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_EDIT)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_SCRIPTS_DELETE)), ): """Delete all unused files from job scripts on the file storage.""" logger.info("Starting garbage collection from jobbergate file storage") diff --git a/jobbergate-api/jobbergate_api/apps/job_submissions/routers.py b/jobbergate-api/jobbergate_api/apps/job_submissions/routers.py index 153956421..613763d70 100644 --- a/jobbergate-api/jobbergate_api/apps/job_submissions/routers.py +++ b/jobbergate-api/jobbergate_api/apps/job_submissions/routers.py @@ -41,7 +41,7 @@ async def job_submission_create( create_request: JobSubmissionCreateRequest, secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SUBMISSIONS_EDIT, ensure_email=True) + secure_services(Permissions.JOB_SUBMISSIONS_CREATE, ensure_email=True) ), ): """ @@ -94,7 +94,7 @@ async def job_submission_create( ) async def job_submission_get( id: int = Path(...), - secure_services: SecureService = Depends(secure_services(Permissions.JOB_SUBMISSIONS_VIEW, commit=False)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_SUBMISSIONS_READ, commit=False)), ): """Return the job_submission given it's id.""" logger.debug(f"Getting job submission {id=}") @@ -120,7 +120,7 @@ async def job_submission_get_list( None, description="Filter job-submissions by the job-script-id they were created from.", ), - secure_services: SecureService = Depends(secure_services(Permissions.JOB_SUBMISSIONS_VIEW, commit=False)), + secure_services: SecureService = Depends(secure_services(Permissions.JOB_SUBMISSIONS_READ, commit=False)), ): """List job_submissions for the authenticated user.""" logger.debug("Fetching job submissions") @@ -155,7 +155,7 @@ async def job_submission_get_list( async def job_submission_delete( id: int = Path(..., description="id of the job submission to delete"), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SUBMISSIONS_EDIT, ensure_email=True) + secure_services(Permissions.JOB_SUBMISSIONS_DELETE, ensure_email=True) ), ): """Delete job_submission given its id.""" @@ -177,7 +177,7 @@ async def job_submission_update( update_params: JobSubmissionUpdateRequest, id: int = Path(), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SUBMISSIONS_EDIT, ensure_email=True) + secure_services(Permissions.JOB_SUBMISSIONS_UPDATE, ensure_email=True) ), ): """Update a job_submission given its id.""" @@ -199,7 +199,7 @@ async def job_submission_agent_update( update_params: JobSubmissionAgentUpdateRequest, id: int = Path(), secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SUBMISSIONS_EDIT, ensure_client_id=True) + secure_services(Permissions.JOB_SUBMISSIONS_UPDATE, ensure_client_id=True) ), ): """ @@ -267,7 +267,7 @@ async def job_submission_agent_update( async def job_submissions_agent_submitted( submitted_request: JobSubmissionAgentSubmittedRequest, secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SUBMISSIONS_EDIT, ensure_client_id=True) + secure_services(Permissions.JOB_SUBMISSIONS_UPDATE, ensure_client_id=True) ), ): """Update a job_submission to indicate that it was submitted to Slurm.""" @@ -301,7 +301,7 @@ async def job_submissions_agent_submitted( async def job_submissions_agent_rejected( rejected_request: JobSubmissionAgentRejectedRequest, secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SUBMISSIONS_EDIT, ensure_client_id=True) + secure_services(Permissions.JOB_SUBMISSIONS_UPDATE, ensure_client_id=True) ), ): """Update a job_submission to indicate that it was rejected by Slurm.""" @@ -346,7 +346,7 @@ async def job_submissions_agent_rejected( ) async def job_submissions_agent_pending( secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SUBMISSIONS_VIEW, commit=False, ensure_client_id=True) + secure_services(Permissions.JOB_SUBMISSIONS_READ, commit=False, ensure_client_id=True) ), ): """Get a list of pending job submissions for the cluster-agent.""" @@ -372,7 +372,7 @@ async def job_submissions_agent_pending( ) async def job_submissions_agent_active( secure_services: SecureService = Depends( - secure_services(Permissions.JOB_SUBMISSIONS_VIEW, commit=False, ensure_client_id=True) + secure_services(Permissions.JOB_SUBMISSIONS_READ, commit=False, ensure_client_id=True) ), ): """Get a list of active job submissions for the cluster-agent.""" diff --git a/jobbergate-api/jobbergate_api/apps/permissions.py b/jobbergate-api/jobbergate_api/apps/permissions.py index a7507c479..e39f9a5bf 100644 --- a/jobbergate-api/jobbergate_api/apps/permissions.py +++ b/jobbergate-api/jobbergate_api/apps/permissions.py @@ -10,9 +10,15 @@ class Permissions(str, Enum): Describe the permissions that may be used for protecting Jobbergate routes. """ - JOB_TEMPLATES_VIEW = "jobbergate:job-templates:view" - JOB_TEMPLATES_EDIT = "jobbergate:job-templates:edit" - JOB_SCRIPTS_VIEW = "jobbergate:job-scripts:view" - JOB_SCRIPTS_EDIT = "jobbergate:job-scripts:edit" - JOB_SUBMISSIONS_VIEW = "jobbergate:job-submissions:view" - JOB_SUBMISSIONS_EDIT = "jobbergate:job-submissions:edit" + JOB_TEMPLATES_CREATE = "jobbergate:job-templates:create" + JOB_TEMPLATES_READ = "jobbergate:job-templates:read" + JOB_TEMPLATES_UPDATE = "jobbergate:job-templates:update" + JOB_TEMPLATES_DELETE = "jobbergate:job-templates:delete" + JOB_SCRIPTS_CREATE = "jobbergate:job-scripts:create" + JOB_SCRIPTS_READ = "jobbergate:job-scripts:read" + JOB_SCRIPTS_UPDATE = "jobbergate:job-scripts:update" + JOB_SCRIPTS_DELETE = "jobbergate:job-scripts:delete" + JOB_SUBMISSIONS_CREATE = "jobbergate:job-submissions:create" + JOB_SUBMISSIONS_READ = "jobbergate:job-submissions:read" + JOB_SUBMISSIONS_UPDATE = "jobbergate:job-submissions:update" + JOB_SUBMISSIONS_DELETE = "jobbergate:job-submissions:delete" diff --git a/jobbergate-api/tests/apps/job_script_templates/test_routers.py b/jobbergate-api/tests/apps/job_script_templates/test_routers.py index b19a62cd4..3b835c441 100644 --- a/jobbergate-api/tests/apps/job_script_templates/test_routers.py +++ b/jobbergate-api/tests/apps/job_script_templates/test_routers.py @@ -29,7 +29,7 @@ async def test_create_job_template__success( ) tester_email = payload.pop("owner_email") - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_CREATE) response = await client.post("jobbergate/job-script-templates", json=payload) assert response.status_code == 201, f"Create failed: {response.text}" @@ -53,7 +53,7 @@ async def test_create_job_template__success( assert instance.template_vars == dict(foo="bar") # Make sure that the data can be retrieved with a GET request - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_VIEW) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_READ) response = await client.get(f"jobbergate/job-script-templates/{instance.id}") assert response.status_code == 200 response_data = response.json() @@ -81,7 +81,7 @@ async def test_create_job_template__fail_identifier_already_exists( payload = fill_job_template_data(identifier="duplicated-template") tester_email = payload.pop("owner_email") - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_CREATE) response = await client.post("jobbergate/job-script-templates", json=payload) assert response.status_code == 201 @@ -104,7 +104,7 @@ async def test_create_job_template__fail_missing_name( payload.pop("name") tester_email = payload.pop("owner_email") - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_CREATE) response = await client.post("jobbergate/job-script-templates", json=payload) @@ -129,7 +129,7 @@ async def test_update_job_template__success( template_vars={"new": "value"}, ) - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_UPDATE) response = await client.put( f"jobbergate/job-script-templates/{instance.id}", json=payload, @@ -156,7 +156,7 @@ async def test_update_job_template__fail_not_found( description="new-description", template_vars={"new": "value"}, ) - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_UPDATE) response = await client.put(f"jobbergate/job-script-templates/{job_template_id}", json=payload) assert response.status_code == 404 @@ -192,7 +192,7 @@ async def test_update_job_template__forbidden( template_vars={"new": "value"}, ) - inject_security_header(requester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(requester_email, Permissions.JOB_TEMPLATES_UPDATE) response = await client.put( f"jobbergate/job-script-templates/{instance.id}", json=payload, @@ -217,7 +217,7 @@ async def test_get_job_template__success( identification = getattr(instance, identification_field) - inject_security_header(instance.owner_email, Permissions.JOB_TEMPLATES_VIEW) + inject_security_header(instance.owner_email, Permissions.JOB_TEMPLATES_READ) response = await client.get(f"jobbergate/job-script-templates/{identification}") assert response.status_code == 200, f"Get failed: {response.text}" response_data = response.json() @@ -244,7 +244,7 @@ async def test_delete_job_template__success( ) instance = await synth_services.crud.template.create(**payload) - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_DELETE) identification = getattr(instance, identification_field) response = await client.delete(f"jobbergate/job-script-templates/{identification}") assert response.status_code == 204, f"Delete failed: {response.text}" @@ -259,7 +259,7 @@ async def test_delete_job_template__fail_not_found( synth_session, ): job_template_id = 0 - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_DELETE) response = await client.delete(f"jobbergate/job-script-templates/{job_template_id}") assert response.status_code == 404 @@ -282,7 +282,7 @@ async def test_delete_job_template__forbidden( ) instance = await synth_services.crud.template.create(**payload) - inject_security_header(requester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(requester_email, Permissions.JOB_TEMPLATES_DELETE) identification = getattr(instance, identification_field) response = await client.delete(f"jobbergate/job-script-templates/{identification}") assert response.status_code == 403 @@ -321,7 +321,7 @@ async def test_clone_job_template__success( new_owner_email = "new_" + tester_email - inject_security_header(new_owner_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(new_owner_email, Permissions.JOB_TEMPLATES_CREATE) response = await client.post(f"jobbergate/job-script-templates/clone/{original_instance.id}") assert response.status_code == 201, f"Clone failed: {response.text}" @@ -364,7 +364,7 @@ async def test_clone_job_template__replace_base_values( template_vars={"new": "value"}, ) - inject_security_header(new_owner_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(new_owner_email, Permissions.JOB_TEMPLATES_CREATE) response = await client.post( f"jobbergate/job-script-templates/clone/{original_instance.id}", json=payload ) @@ -398,7 +398,7 @@ async def test_clone_job_template__fail_not_found( synth_services, ): assert (await synth_services.crud.template.count()) == 0 - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_CREATE) response = await client.post("jobbergate/job-script-templates/clone/0") assert response.status_code == 404 @@ -416,7 +416,7 @@ async def test_clone_job_template__fail_conflict( **fill_job_template_data(owner_email=tester_email, identifier=identifier) ) - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_CREATE) response = await client.post( f"jobbergate/job-script-templates/clone/{original_instance.id}", json=dict(identifier=identifier) ) @@ -471,7 +471,7 @@ async def test_list_job_templates__all_success( inject_security_header, job_templates_list, ): - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_VIEW) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_READ) response = await client.get( "jobbergate/job-script-templates", params=dict(include_null_identifier=True, include_archived=True, sort_field="id"), @@ -498,7 +498,7 @@ async def test_list_job_templates__ignore_archived( inject_security_header, job_templates_list, ): - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_VIEW) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_READ) response = await client.get("jobbergate/job-script-templates?include_null_identifier=True") assert response.status_code == 200, f"List failed: {response.text}" @@ -517,7 +517,7 @@ async def test_list_job_templates__user_only( inject_security_header, job_templates_list, ): - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_VIEW) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_READ) response = await client.get( "jobbergate/job-script-templates?user_only=True&include_null_identifier=True&include_archived=True" ) @@ -552,7 +552,7 @@ async def test_create__success( file_type = "ENTRYPOINT" dummy_file_path = make_dummy_file("test_template.py.j2", content=dummy_template) - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_CREATE) with open(dummy_file_path, mode="rb") as template_file: response = await client.put( f"jobbergate/job-script-templates/{parent_id}/upload/template/{file_type}", @@ -574,7 +574,7 @@ async def test_create__success( assert dummy_template == file_content.decode() # Finally, see that the file is included in the parent template file list - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_VIEW) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_READ) response = await client.get(f"jobbergate/job-script-templates/{parent_id}") assert response.status_code == status.HTTP_200_OK, f"Get failed: {response.text}" @@ -603,7 +603,7 @@ async def test_create__fail_forbidden( owner_email = tester_email requester_email = "another_" + owner_email - inject_security_header(requester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(requester_email, Permissions.JOB_TEMPLATES_CREATE) with open(dummy_file_path, mode="rb") as template_file: response = await client.put( f"jobbergate/job-script-templates/{parent_id}/upload/template/{file_type}", @@ -628,7 +628,7 @@ async def test_get__success( file_type="ENTRYPOINT", ) - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_VIEW) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_READ) response = await client.get( f"jobbergate/job-script-templates/{job_template_data.id}/upload/template/test_template.py.j2" ) @@ -653,7 +653,7 @@ async def test_delete__success( file_type="ENTRYPOINT", ) - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_DELETE) response = await client.delete( f"jobbergate/job-script-templates/{parent_id}/upload/template/test_template.py.j2" ) @@ -682,7 +682,7 @@ async def test_delete__fail_forbidden( owner_email = tester_email requester_email = "another_" + owner_email - inject_security_header(requester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(requester_email, Permissions.JOB_TEMPLATES_DELETE) response = await client.delete( f"jobbergate/job-script-templates/{parent_id}/upload/template/test_template.py.j2" ) @@ -709,7 +709,7 @@ async def test_create__success( dummy_file_path = make_dummy_file("test_template.py.j2", content=dummy_application_source_file) runtime_config = {"foo": "bar"} - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_CREATE) with open(dummy_file_path, mode="rb") as workflow_file: response = await client.put( f"jobbergate/job-script-templates/{parent_id}/upload/workflow", @@ -732,7 +732,7 @@ async def test_create__success( assert dummy_application_source_file == file_content.decode() # Finally, see that the file is included in the parent template file list - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_VIEW) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_READ) response = await client.get(f"jobbergate/job-script-templates/{parent_id}") assert response.status_code == status.HTTP_200_OK, f"Get failed: {response.text}" @@ -768,7 +768,7 @@ async def test_update__success( new_runtime_config = {"new": "config"} new_content = "import that" - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_CREATE) with open(make_dummy_file("test_template.py.j2", content=new_content), mode="rb") as workflow_file: response = await client.put( f"jobbergate/job-script-templates/{parent_id}/upload/workflow", @@ -808,7 +808,7 @@ async def test_update_optional_runtime_config__success( new_content = "import that" - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_CREATE) with open(make_dummy_file("test_template.py.j2", content=new_content), mode="rb") as workflow_file: response = await client.put( f"jobbergate/job-script-templates/{parent_id}/upload/workflow", @@ -838,7 +838,7 @@ async def test_update_optional_runtime_config__fail_on_creation_time( new_content = "import that" - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_CREATE) with open(make_dummy_file("test_template.py.j2", content=new_content), mode="rb") as workflow_file: response = await client.put( f"jobbergate/job-script-templates/{parent_id}/upload/workflow", @@ -870,7 +870,7 @@ async def test_create__fail_forbidden( owner_email = tester_email requester_email = "another_" + owner_email - inject_security_header(requester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(requester_email, Permissions.JOB_TEMPLATES_CREATE) with open(dummy_file_path, mode="rb") as workflow_file: response = await client.put( f"jobbergate/job-script-templates/{parent_id}/upload/workflow", @@ -892,7 +892,7 @@ async def test_get__success( runtime_config=dict(foo="bar"), ) - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_VIEW) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_READ) response = await client.get(f"jobbergate/job-script-templates/{parent_id}/upload/workflow") assert response.status_code == status.HTTP_200_OK, f"Get failed: {response.text}" @@ -915,7 +915,7 @@ async def test_delete__success( runtime_config=dict(foo="bar"), ) - inject_security_header(tester_email, Permissions.JOB_TEMPLATES_EDIT) + inject_security_header(tester_email, Permissions.JOB_TEMPLATES_DELETE) response = await client.delete(f"jobbergate/job-script-templates/{parent_id}/upload/workflow") assert response.status_code == status.HTTP_200_OK, f"Delete failed: {response.text}" diff --git a/jobbergate-api/tests/apps/job_scripts/test_routers.py b/jobbergate-api/tests/apps/job_scripts/test_routers.py index 2cacb0b9f..0ac0f69f3 100644 --- a/jobbergate-api/tests/apps/job_scripts/test_routers.py +++ b/jobbergate-api/tests/apps/job_scripts/test_routers.py @@ -17,7 +17,7 @@ async def test_create_stand_alone_job_script( payload = fill_job_script_data() tester_email = payload.pop("owner_email") - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_CREATE) response = await client.post("jobbergate/job-scripts", json=payload) assert response.status_code == 201, f"Create failed: {response.text}" @@ -41,7 +41,7 @@ async def test_create_stand_alone_job_script( assert instance.parent_template_id is None # Make sure that the data can be retrieved with a GET request - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_READ) response = await client.get(f"jobbergate/job-scripts/{created_id}") assert response.status_code == 200 response_data = response.json() @@ -74,7 +74,7 @@ async def test_clone_job_script__success( new_owner_email = "new_" + tester_email - inject_security_header(new_owner_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(new_owner_email, Permissions.JOB_SCRIPTS_CREATE) response = await client.post(f"jobbergate/job-scripts/clone/{original_instance.id}") assert response.status_code == 201, f"Clone failed: {response.text}" @@ -105,7 +105,7 @@ async def test_clone_job_script__replace_base_values( description="new description", ) - inject_security_header(new_owner_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(new_owner_email, Permissions.JOB_SCRIPTS_CREATE) response = await client.post(f"jobbergate/job-scripts/clone/{original_instance.id}", json=payload) assert response.status_code == 201, f"Clone failed: {response.text}" @@ -132,7 +132,7 @@ async def test_clone_job_script__fail_not_found( synth_services, ): assert (await synth_services.crud.job_script.count()) == 0 - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_CREATE) response = await client.post("jobbergate/job-scripts/clone/0") assert response.status_code == 404 @@ -175,7 +175,7 @@ async def test_render_job_script_from_template( }, } - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_CREATE) response = await client.post( f"/jobbergate/job-scripts/render-from-template/{base_template.id}", json=payload, @@ -229,7 +229,7 @@ async def test_render_job_script_from_template__no_entrypoint( }, } - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_CREATE) response = await client.post( f"/jobbergate/job-scripts/render-from-template/{base_template.id}", json=payload, @@ -281,7 +281,7 @@ async def test_render_job_script_from_template__multiple_entrypoints( }, } - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_CREATE) response = await client.post( f"/jobbergate/job-scripts/render-from-template/{base_template.id}", json=payload, @@ -316,7 +316,7 @@ async def test_render_job_script_from_template__template_file_unavailable( }, } - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_CREATE) response = await client.post( f"/jobbergate/job-scripts/render-from-template/{base_template.id}", json=payload, @@ -381,7 +381,7 @@ async def test_render_job_script_from_template__without_template( }, } - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_CREATE) response = await client.post( "/jobbergate/job-scripts/render-from-template/9999", json=payload, @@ -406,7 +406,7 @@ async def test_get_job_script_by_id__success( """ inserted_instance = await synth_services.crud.job_script.create(**fill_job_script_data()) - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_READ) response = await client.get(f"/jobbergate/job-scripts/{inserted_instance.id}") assert response.status_code == status.HTTP_200_OK, f"Get failed: {response.text}" @@ -432,7 +432,7 @@ async def test_get_job_script_by_id__invalid_id( requested job_script does not exist. We show this by asserting that the status code returned is what we would expect given the job_script requested doesn't exist (404). """ - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_READ) response = await client.get("/jobbergate/job-scripts/9999") assert response.status_code == status.HTTP_404_NOT_FOUND @@ -502,7 +502,7 @@ async def test_list_job_scripts__all_success( inject_security_header, job_scripts_list, ): - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_READ) response = await client.get("jobbergate/job-scripts?include_archived=True&sort_field=id") assert response.status_code == 200, f"Get failed: {response.text}" @@ -526,7 +526,7 @@ async def test_list_job_scripts__ignore_archived( inject_security_header, job_scripts_list, ): - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_READ) response = await client.get("jobbergate/job-scripts") assert response.status_code == 200, f"Get failed: {response.text}" @@ -545,7 +545,7 @@ async def test_list_job_scripts__user_only( inject_security_header, job_scripts_list, ): - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_READ) response = await client.get("jobbergate/job-scripts?user_only=True&include_archived=True") assert response.status_code == 200, f"Get failed: {response.text}" @@ -588,7 +588,7 @@ async def test_list_job_scripts__include_parent( file_type=file_type, ) - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_READ) response = await client.get("jobbergate/job-scripts", params={"include_parent": True}) assert response.status_code == 200, f"Get failed: {response.text}" @@ -623,7 +623,7 @@ async def test_list_job_scripts__not_include_parent( for item in data: await synth_services.crud.job_script.create(**item) - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_READ) response = await client.get("jobbergate/job-scripts", params={"include_parent": False}) assert response.status_code == 200, f"Get failed: {response.text}" @@ -669,7 +669,7 @@ async def test_list_job_scripts__search( for item in data: await synth_services.crud.job_script.create(**item) - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_READ) response = await client.get("jobbergate/job-scripts", params={"search": "instance"}) assert response.status_code == 200, f"Get failed: {response.text}" @@ -702,7 +702,7 @@ async def test_create__success( file_type = "ENTRYPOINT" dummy_file_path = make_dummy_file("test_template.sh", content=job_script_data_as_string) - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_CREATE) with open(dummy_file_path, mode="rb") as file: response = await client.put( f"jobbergate/job-scripts/{id}/upload/{file_type}", @@ -738,7 +738,7 @@ async def test_create__fail_forbidden( owner_email = tester_email requester_email = "another_" + owner_email - inject_security_header(requester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(requester_email, Permissions.JOB_SCRIPTS_CREATE) with open(dummy_file_path, mode="rb") as file: response = await client.put( f"jobbergate/job-scripts/{id}/upload/{file_type}", @@ -767,7 +767,7 @@ async def test_get__success( file_type=file_type, ) - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_READ) response = await client.get(f"jobbergate/job-scripts/{id}/upload/{job_script_filename}") assert response.status_code == status.HTTP_200_OK @@ -794,7 +794,7 @@ async def test_delete__success( file_type=file_type, ) - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_DELETE) response = await client.delete(f"jobbergate/job-scripts/{parent_id}/upload/{job_script_filename}") assert response.status_code == status.HTTP_200_OK, f"Delete failed: {response.text}" @@ -825,13 +825,13 @@ async def test_delete__fail_forbidden( owner_email = tester_email requester_email = "another_" + owner_email - inject_security_header(requester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(requester_email, Permissions.JOB_SCRIPTS_DELETE) response = await client.delete(f"jobbergate/job-scripts/{parent_id}/upload/{job_script_filename}") assert response.status_code == status.HTTP_403_FORBIDDEN async def test_auto_clean_unused_entries(client, tester_email, inject_security_header, synth_session): """Test that unused job scripts are automatically cleaned.""" - inject_security_header(tester_email, Permissions.JOB_SCRIPTS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SCRIPTS_DELETE) response = await client.delete("jobbergate/job-scripts/clean-unused-entries") assert response.status_code == status.HTTP_202_ACCEPTED diff --git a/jobbergate-api/tests/apps/job_submissions/test_routers.py b/jobbergate-api/tests/apps/job_submissions/test_routers.py index 9cf503736..d6f75d844 100644 --- a/jobbergate-api/tests/apps/job_submissions/test_routers.py +++ b/jobbergate-api/tests/apps/job_submissions/test_routers.py @@ -48,7 +48,7 @@ async def test_create_job_submission__on_site_submission( inserted_job_script_id = base_job_script.id slurm_job_id = 1234 - inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-cluster-client") + inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_CREATE, client_id="dummy-cluster-client") create_data = fill_job_submission_data(job_script_id=inserted_job_script_id, slurm_job_id=slurm_job_id) # Removed defaults to make sure these are correctly set by other mechanisms @@ -107,7 +107,7 @@ async def test_create_job_submission__with_client_id_in_token( inserted_job_script_id = base_job_script.id - inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-cluster-client") + inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_CREATE, client_id="dummy-cluster-client") create_data = fill_job_submission_data(job_script_id=inserted_job_script_id) # Removed defaults to make sure these are correctly set by other mechanisms @@ -136,7 +136,7 @@ async def test_create_job_submission__with_client_id_in_token( created_id = response_data["id"] # Make sure that the data can be retrieved with a GET request - inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_READ) response = await client.get(f"jobbergate/job-submissions/{created_id}") assert response.status_code == 200 response_data = response.json() @@ -180,7 +180,7 @@ async def test_create_job_submission__with_client_id_in_request_body( inserted_job_script_id = base_job_script.id - inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-cluster-client") + inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_CREATE, client_id="dummy-cluster-client") create_data = fill_job_submission_data(job_script_id=inserted_job_script_id) # Removed defaults to make sure these are correctly set by other mechanisms @@ -238,7 +238,7 @@ async def test_create_job_submission__with_execution_directory( inserted_job_script_id = base_job_script.id - inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-cluster-client") + inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_CREATE, client_id="dummy-cluster-client") create_data = fill_job_submission_data( job_script_id=inserted_job_script_id, execution_directory="/some/fake/path", @@ -280,7 +280,7 @@ async def test_create_job_submission_without_job_script( We show this by trying to create a job_submission without a job_script created before, then assert that the job_submission still does not exists in the database, the correct status code (404) is returned. """ - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_EDIT) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_CREATE) response = await client.post( "/jobbergate/job-submissions", json=fill_job_submission_data(job_script_id=9999) ) @@ -334,7 +334,7 @@ async def test_create_job_submission_without_client_id( inject_security_header( tester_email, - Permissions.JOB_SUBMISSIONS_EDIT, + Permissions.JOB_SUBMISSIONS_CREATE, ) create_data = fill_job_submission_data(job_script_id=inserted_job_script_id) create_data.pop("client_id", None) @@ -364,7 +364,7 @@ async def test_get_job_submission_by_id( inserted_instance = await synth_services.crud.job_submission.create(**fill_job_submission_data()) inserted_job_submission_id = inserted_instance.id - inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_READ) response = await client.get(f"/jobbergate/job-submissions/{inserted_job_submission_id}") assert response.status_code == status.HTTP_200_OK @@ -404,7 +404,7 @@ async def test_get_job_submission_by_id_invalid(client, inject_security_header, requested job_submission does not exist. We show this by asserting that the status code returned is what we would expect given the job_submission requested doesn't exist (404). """ - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_READ) response = await client.get("/jobbergate/job-submissions/9999") assert response.status_code == status.HTTP_404_NOT_FOUND @@ -450,7 +450,7 @@ async def test_get_job_submissions__no_param( for create_data in all_create_data: await synth_services.crud.job_submission.create(**create_data) - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_READ) response = await client.get("/jobbergate/job-submissions") assert unpack_response(response, key="name", sort=True) == ["sub1", "sub2", "sub3"] @@ -536,7 +536,7 @@ async def test_get_job_submissions__user_only( for create_data in all_create_data: await synth_services.crud.job_submission.create(**create_data) - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_READ) response = await client.get("/jobbergate/job-submissions", params=dict(user_only=True)) assert unpack_response(response, key="name", sort=True) == ["sub1", "sub3"] @@ -560,7 +560,7 @@ async def test_get_job_submissions__include_archived( for create_data in all_create_data: await synth_services.crud.job_submission.create(**create_data) - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_READ) response = await client.get("/jobbergate/job-submissions", params=dict(user_only=False)) assert unpack_response(response, key="is_archived", sort=True) == [False, True] @@ -586,7 +586,7 @@ async def test_get_job_submissions__from_job_script_id( **fill_job_submission_data(job_script_id=job_script_list[i // 2].id) ) - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_READ) for job_script in job_script_list: response = await client.get(f"/jobbergate/job-submissions?from_job_script_id={job_script.id}") @@ -636,7 +636,7 @@ async def test_get_job_submissions__with_status_param( ), ) - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_READ) response = await client.get( "/jobbergate/job-submissions", params=dict(submit_status=JobSubmissionStatus.CREATED.value) ) @@ -676,7 +676,7 @@ async def test_get_job_submissions__with_search_param( for item in submission_list: await synth_services.crud.job_submission.create(job_script_id=inserted_job_script_id, **item) - inject_security_header("admin@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("admin@org.com", Permissions.JOB_SUBMISSIONS_READ) response = await client.get("/jobbergate/job-submissions?all=true&search=one") assert unpack_response(response, key="name") == ["test name one"] @@ -726,7 +726,7 @@ async def test_get_job_submissions_with_sort_params( for item in submission_list: await synth_services.crud.job_submission.create(job_script_id=inserted_job_script_id, **item) - inject_security_header("admin@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("admin@org.com", Permissions.JOB_SUBMISSIONS_READ) response = await client.get("/jobbergate/job-submissions?sort_field=id") assert unpack_response(response, key="name") == ["Z", "Y", "X"] @@ -777,7 +777,7 @@ async def test_list_job_submission_pagination( for item in submission_list: await synth_services.crud.job_submission.create(**item) - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_READ) response = await client.get("/jobbergate/job-submissions?page=1&size=1&sort_field=id") assert unpack_response( response, @@ -854,7 +854,7 @@ async def test_get_job_submissions_with_slurm_job_ids_param( for item in submission_list: await synth_services.crud.job_submission.create(job_script_id=inserted_job_script_id, **item) - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_READ) response = await client.get("/jobbergate/job-submissions?slurm_job_ids=101,103") assert unpack_response(response, key="name", sort=True) == ["sub1", "sub3"] @@ -894,7 +894,7 @@ async def test_get_job_submissions_applies_no_slurm_filter_if_slurm_job_ids_is_e for item in submission_list: await synth_services.crud.job_submission.create(job_script_id=inserted_job_script_id, **item) - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_READ) with_empty_param_response = await client.get("/jobbergate/job-submissions?slurm_job_ids=") assert with_empty_param_response.status_code == status.HTTP_200_OK @@ -916,7 +916,7 @@ async def test_get_job_submissions_with_invalid_slurm_job_ids_param( This test proves that GET /job-submissions requires the slurm_job_ids parameter to be a comma-separated list of integer slurm job ids. """ - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_READ) response = await client.get("/jobbergate/job-submissions?slurm_job_ids=101-103") assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY assert "Invalid slurm_job_ids" in response.text @@ -952,7 +952,7 @@ async def test_update_job_submission__basic( payload = dict(name="new name", description="new description", execution_directory="/some/fake/path") - inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_UPDATE) response = await client.put(f"/jobbergate/job-submissions/{inserted_job_submission_id}", json=payload) assert response.status_code == status.HTTP_200_OK, f"Update failed: {response.text}" @@ -977,7 +977,7 @@ async def test_update_job_submission_not_found(client, inject_security_header, s This test proves that it is not possible to update a job_submission if it is not found. We show this by asserting that the response status code of the request is 404. """ - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_EDIT) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_UPDATE) response = await client.put("/jobbergate/job-submissions/9999", json=dict(job_submission_name="new name")) assert response.status_code == status.HTTP_404_NOT_FOUND @@ -1078,7 +1078,7 @@ async def test_delete_job_submission( ) inserted_job_submission_id = inserted_submission.id - inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_EDIT) + inject_security_header(tester_email, Permissions.JOB_SUBMISSIONS_DELETE) response = await client.delete(f"/jobbergate/job-submissions/{inserted_job_submission_id}") assert response.status_code == status.HTTP_204_NO_CONTENT @@ -1093,7 +1093,7 @@ async def test_delete_job_submission_not_found(client, inject_security_header, s This test proves that it is not possible to delete a job_submission if it does not exists. We show this by asserting that a 404 response status code is returned. """ - inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_EDIT) + inject_security_header("owner1@org.com", Permissions.JOB_SUBMISSIONS_DELETE) response = await client.delete("/jobbergate/job-submissions/9999") assert response.status_code == status.HTTP_404_NOT_FOUND @@ -1224,7 +1224,7 @@ async def test_job_submissions_agent_pending__success( inject_security_header( "who@cares.com", - Permissions.JOB_SUBMISSIONS_VIEW, + Permissions.JOB_SUBMISSIONS_READ, client_id="dummy-client", ) response = await client.get("/jobbergate/job-submissions/agent/pending") @@ -1255,7 +1255,7 @@ async def test_job_submissions_agent_pending__returns_400_if_token_does_not_carr """ inject_security_header( "who@cares.com", - Permissions.JOB_SUBMISSIONS_VIEW, + Permissions.JOB_SUBMISSIONS_READ, ) response = await client.get("/jobbergate/job-submissions/agent/pending") assert response.status_code == status.HTTP_400_BAD_REQUEST @@ -1291,7 +1291,7 @@ async def test_job_submissions_agent_submitted__success( slurm_job_id=111, ) - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-client") + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE, client_id="dummy-client") response = await client.post(f"/jobbergate/job-submissions/agent/submitted", json=payload) assert response.status_code == status.HTTP_202_ACCEPTED @@ -1332,7 +1332,7 @@ async def test_job_submissions_agent_submitted__fails_if_status_is_not_CREATED( slurm_job_id=111, ) - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-client") + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE, client_id="dummy-client") response = await client.post(f"/jobbergate/job-submissions/agent/submitted", json=payload) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "Only CREATED Job Submissions can be marked as SUBMITTED" in response.text @@ -1348,7 +1348,7 @@ async def test_job_submissions_agent_submitted__fails_if_token_does_not_carry_cl This test proves that PUT /job-submissions/agent/submitted returns a 400 status if the access token used to query the route does not include a ``client_id``. """ - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT) + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE) response = await client.put( "/jobbergate/job-submissions/agent/1", json=dict(status=JobSubmissionStatus.SUBMITTED, slurm_job_id=111), @@ -1385,7 +1385,7 @@ async def test_job_submissions_agent_submitted__fails_if_client_id_does_not_matc slurm_job_id=111, ) - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT, client_id="stupid-client") + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE, client_id="stupid-client") response = await client.post(f"/jobbergate/job-submissions/agent/submitted", json=payload) assert response.status_code == status.HTTP_403_FORBIDDEN @@ -1425,7 +1425,7 @@ async def test_job_submissions_agent_rejected__success( inject_security_header( "who@cares.com", - Permissions.JOB_SUBMISSIONS_EDIT, + Permissions.JOB_SUBMISSIONS_UPDATE, client_id="dummy-client", organization_id="dummy-org", ) @@ -1475,7 +1475,7 @@ async def test_job_submissions_agent_rejected__publishes_status_change_to_rabbit inject_security_header( "who@cares.com", - Permissions.JOB_SUBMISSIONS_EDIT, + Permissions.JOB_SUBMISSIONS_UPDATE, client_id="dummy-client", organization_id="dummy-org", ) @@ -1529,7 +1529,7 @@ async def test_job_submissions_agent_rejected__fails_if_status_is_not_CREATED( report_message="Something went wrong", ) - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-client") + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE, client_id="dummy-client") response = await client.post(f"/jobbergate/job-submissions/agent/rejected", json=payload) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "Only CREATED Job Submissions can be marked as REJECTED" in response.text @@ -1566,7 +1566,7 @@ async def test_job_submissions_agent_update__success( ) inserted_job_submission_id = inserted_submission.id - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-client") + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE, client_id="dummy-client") response = await client.put( f"/jobbergate/job-submissions/agent/{inserted_job_submission_id}", json=dict( @@ -1630,7 +1630,7 @@ async def test_job_submissions_agent_update__sets_aborted_status( ) inserted_job_submission_id = inserted_submission.id - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-client") + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE, client_id="dummy-client") response = await client.put( f"/jobbergate/job-submissions/agent/{inserted_job_submission_id}", json=dict( @@ -1681,7 +1681,7 @@ async def test_job_submissions_agent_update__sets_done_status( ) inserted_job_submission_id = inserted_submission.id - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-client") + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE, client_id="dummy-client") response = await client.put( f"/jobbergate/job-submissions/agent/{inserted_job_submission_id}", json=dict( @@ -1745,7 +1745,7 @@ async def test_job_submissions_agent_update__publishes_status_change_to_rabbitmq inject_security_header( "who@cares.com", - Permissions.JOB_SUBMISSIONS_EDIT, + Permissions.JOB_SUBMISSIONS_UPDATE, client_id="dummy-client", organization_id="dummy-org", ) @@ -1786,7 +1786,7 @@ async def test_job_submissions_agent_update__returns_400_if_token_does_not_carry This test proves that PUT /job-submissions/agent/{job_submission_id} returns a 400 status if the access token used to query the route does not include a ``client_id``. """ - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT) + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE) response = await client.put( "/jobbergate/job-submissions/agent/1", json=dict(status=JobSubmissionStatus.SUBMITTED, slurm_job_id=111), @@ -1821,7 +1821,7 @@ async def test_job_submissions_agent_update__returns_403_if_client_id_differs( ) inserted_job_submission_id = inserted_submission.id - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT, client_id="stupid-client") + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE, client_id="stupid-client") response = await client.put( f"/jobbergate/job-submissions/agent/{inserted_job_submission_id}", json=dict( @@ -1859,7 +1859,7 @@ async def test_job_submissions_agent_update__returns_409_if_slurm_job_id_differs ) inserted_job_submission_id = inserted_submission.id - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT, client_id="dummy-client") + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_UPDATE, client_id="dummy-client") response = await client.put( f"/jobbergate/job-submissions/agent/{inserted_job_submission_id}", json=dict( @@ -1920,7 +1920,7 @@ async def test_job_submissions_agent_active__success( for item in submission_list: await synth_services.crud.job_submission.create(job_script_id=inserted_job_script_id, **item) - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_VIEW, client_id="dummy-client") + inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_READ, client_id="dummy-client") response = await client.get("/jobbergate/job-submissions/agent/active") assert response.status_code == status.HTTP_200_OK, f"Get failed: {response.text}" @@ -1941,7 +1941,7 @@ async def test_job_submissions_agent_active__returns_400_if_token_does_not_carry """ inject_security_header( "who@cares.com", - Permissions.JOB_SUBMISSIONS_VIEW, + Permissions.JOB_SUBMISSIONS_READ, ) response = await client.get("/jobbergate/job-submissions/agent/active") assert response.status_code == status.HTTP_400_BAD_REQUEST diff --git a/jobbergate-composed/etc/jobbergate-local.json b/jobbergate-composed/etc/jobbergate-local.json index c871e025d..7fbac6514 100755 --- a/jobbergate-composed/etc/jobbergate-local.json +++ b/jobbergate-composed/etc/jobbergate-local.json @@ -88,24 +88,40 @@ "client": { "cli": [ { - "id": "3ed61d85-0d03-46a8-8010-e0be1e9931fa", - "name": "jobbergate:job-submissions:view", + "id": "3494f738-d92c-4b19-9e25-5590ab01f086", + "name": "jobbergate:job-templates:create", "composite": false, "clientRole": true, "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", "attributes": {} }, { - "id": "191708ce-ba2a-4df1-ada4-d729260db855", - "name": "jobbergate:job-scripts:view", + "id": "74be4480-5aa8-4df4-a4f4-04ee9c509306", + "name": "jobbergate:job-templates:read", + "composite": false, + "clientRole": true, + "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", + "attributes": {} + }, + { + "id": "c0c9190a-9df0-4d8a-9c75-da16773ba899", + "name": "jobbergate:job-templates:update", "composite": false, "clientRole": true, "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", "attributes": {} }, { - "id": "9eaccc11-12fd-433f-8ecd-fab1f5abb429", - "name": "jobbergate:job-templates:edit", + "id": "30e1234f-24dc-4d50-a710-773822b47733", + "name": "jobbergate:job-templates:delete", + "composite": false, + "clientRole": true, + "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", + "attributes": {} + }, + { + "id": "191708ce-ba2a-4df1-ada4-d729260db855", + "name": "jobbergate:job-scripts:create", "composite": false, "clientRole": true, "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", @@ -113,15 +129,31 @@ }, { "id": "4e257fac-e62f-4091-8147-75a0173e58fe", - "name": "jobbergate:job-scripts:edit", + "name": "jobbergate:job-scripts:read", + "composite": false, + "clientRole": true, + "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", + "attributes": {} + }, + { + "id": "058097f0-e692-4026-af1c-33c84a26fdd5", + "name": "jobbergate:job-scripts:update", + "composite": false, + "clientRole": true, + "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", + "attributes": {} + }, + { + "id": "86d55573-97e0-4244-98e6-0201f1308344", + "name": "jobbergate:job-scripts:delete", "composite": false, "clientRole": true, "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", "attributes": {} }, { - "id": "f432b492-a166-4b67-a4a0-a9077ee459d4", - "name": "jobbergate:job-templates:view", + "id": "3ed61d85-0d03-46a8-8010-e0be1e9931fa", + "name": "jobbergate:job-submissions:create", "composite": false, "clientRole": true, "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", @@ -129,7 +161,23 @@ }, { "id": "386f16b0-f358-41dd-a6c2-4f3eb96bc84f", - "name": "jobbergate:job-submissions:edit", + "name": "jobbergate:job-submissions:read", + "composite": false, + "clientRole": true, + "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", + "attributes": {} + }, + { + "id": "7ed00337-05c7-4981-9100-9ff85bf3b031", + "name": "jobbergate:job-submissions:update", + "composite": false, + "clientRole": true, + "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", + "attributes": {} + }, + { + "id": "bde6151d-e30f-49b6-b032-c7bc5401beec", + "name": "jobbergate:job-submissions:delete", "composite": false, "clientRole": true, "containerId": "7f75edc2-e695-481e-b3bd-226fb2958112", @@ -350,48 +398,96 @@ ], "local-slurm": [ { - "id": "82d5fa04-bf1d-4e07-ac0b-47e974d52c04", - "name": "jobbergate:job-templates:view", + "id": "e6e06eff-bc07-433f-8059-69ba1e7e4c6d", + "name": "jobbergate:job-templates:create", + "composite": false, + "clientRole": true, + "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", + "attributes": {} + }, + { + "id": "e5e6b03c-b91f-4265-812a-b5a8d47cdc02", + "name": "jobbergate:job-templates:read", + "composite": false, + "clientRole": true, + "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", + "attributes": {} + }, + { + "id": "1efe5876-e0a4-4f1b-a9bf-5799cab00ddc", + "name": "jobbergate:job-templates:update", + "composite": false, + "clientRole": true, + "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", + "attributes": {} + }, + { + "id": "5bf25b73-7aa7-4015-a050-b8426f10fc20", + "name": "jobbergate:job-templates:delete", + "composite": false, + "clientRole": true, + "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", + "attributes": {} + }, + { + "id": "c38af99d-632f-4313-8fa2-e492b4b3af2d", + "name": "jobbergate:job-scripts:create", + "composite": false, + "clientRole": true, + "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", + "attributes": {} + }, + { + "id": "2b2c4a91-8778-4597-a5da-c0ac9be7a13e", + "name": "jobbergate:job-scripts:read", + "composite": false, + "clientRole": true, + "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", + "attributes": {} + }, + { + "id": "0d157278-f2ec-4458-ae0a-f83bfb865e63", + "name": "jobbergate:job-scripts:update", "composite": false, "clientRole": true, "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", "attributes": {} }, { - "id": "4a53e4d7-e14e-41bf-8fd2-9d377419baca", - "name": "jobbergate:job-templates:edit", + "id": "0ee2314b-bfe8-400f-8ad9-f5e27e4e7c8e", + "name": "jobbergate:job-scripts:delete", "composite": false, "clientRole": true, "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", "attributes": {} }, { - "id": "7063e39b-9d7c-4953-8ab7-750e18c6ff98", - "name": "jobbergate:job-scripts:edit", + "id": "d471a865-9af5-45ad-8828-397828f8e9f0", + "name": "jobbergate:job-submissions:create", "composite": false, "clientRole": true, "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", "attributes": {} }, { - "id": "cb7c4d6c-4690-4cb5-b288-a71557aa5552", - "name": "jobbergate:job-submissions:view", + "id": "7959e27a-64a8-4a68-ad3d-ab554020d8d0", + "name": "jobbergate:job-submissions:read", "composite": false, "clientRole": true, "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", "attributes": {} }, { - "id": "342a9b6f-8406-42d8-b336-697818672a3f", - "name": "jobbergate:job-scripts:view", + "id": "7ef70c60-45a2-4124-8ab3-620c79109ad6", + "name": "jobbergate:job-submissions:update", "composite": false, "clientRole": true, "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", "attributes": {} }, { - "id": "9726a419-92cd-486a-90c5-e4b536066a28", - "name": "jobbergate:job-submissions:edit", + "id": "c3a617a2-607c-4f37-a909-4730828388de", + "name": "jobbergate:job-submissions:delete", "composite": false, "clientRole": true, "containerId": "92da95dd-3e4e-4143-8cbb-b4704bd88438", @@ -566,12 +662,18 @@ ], "clientRoles": { "cli": [ - "jobbergate:job-submissions:view", - "jobbergate:job-scripts:view", - "jobbergate:job-templates:edit", - "jobbergate:job-scripts:edit", - "jobbergate:job-templates:view", - "jobbergate:job-submissions:edit" + "jobbergate:job-scripts:create", + "jobbergate:job-scripts:read", + "jobbergate:job-scripts:update", + "jobbergate:job-scripts:delete", + "jobbergate:job-submissions:create", + "jobbergate:job-submissions:read", + "jobbergate:job-submissions:update", + "jobbergate:job-submissions:delete", + "jobbergate:job-templates:create", + "jobbergate:job-templates:read", + "jobbergate:job-templates:update", + "jobbergate:job-templates:delete" ] }, "notBefore": 0, @@ -592,12 +694,18 @@ ], "clientRoles": { "local-slurm": [ - "jobbergate:job-templates:view", - "jobbergate:job-templates:edit", - "jobbergate:job-scripts:edit", - "jobbergate:job-submissions:view", - "jobbergate:job-scripts:view", - "jobbergate:job-submissions:edit" + "jobbergate:job-scripts:create", + "jobbergate:job-scripts:read", + "jobbergate:job-scripts:update", + "jobbergate:job-scripts:delete", + "jobbergate:job-submissions:create", + "jobbergate:job-submissions:read", + "jobbergate:job-submissions:update", + "jobbergate:job-submissions:delete", + "jobbergate:job-templates:create", + "jobbergate:job-templates:read", + "jobbergate:job-templates:update", + "jobbergate:job-templates:delete" ] }, "notBefore": 0, @@ -2473,4 +2581,4 @@ "clientPolicies": { "policies": [] } -} \ No newline at end of file +}