From 5535e3a1216f5e497c341c70c46a76f6bd25f55c Mon Sep 17 00:00:00 2001 From: Tucker Beck Date: Wed, 3 Apr 2024 17:15:08 -0700 Subject: [PATCH 1/4] PENG-2159 Expanded permission sets Remapped permissions to expand from a view/edit paradigm to individual permissions for create/read/update/delete. Updated the API with the permission sets. Updated jobbergate-composed to use the new permission sets. --- jobbergate-api/CHANGELOG.md | 2 + .../apps/job_script_templates/routers.py | 26 +-- .../apps/job_scripts/routers.py | 24 +-- .../apps/job_submissions/routers.py | 20 +- .../jobbergate_api/apps/permissions.py | 18 +- .../apps/job_script_templates/test_routers.py | 64 +++---- .../tests/apps/job_scripts/test_routers.py | 48 ++--- .../apps/job_submissions/test_routers.py | 84 ++++----- jobbergate-composed/etc/jobbergate-local.json | 178 ++++++++++++++---- 9 files changed, 290 insertions(+), 174 deletions(-) diff --git a/jobbergate-api/CHANGELOG.md b/jobbergate-api/CHANGELOG.md index 565233e3..2e709c64 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.1.0 -- 2024-04-19 - Added cluster statuses table and endpoints to monitor if the agents are pinging the API in the expected time interval [ASP-4600] 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 2db0905c..52d75604 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 d136553d..c25bf3c0 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 e4138715..1fed3e90 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.""" @@ -304,7 +304,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.""" @@ -349,7 +349,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.""" @@ -375,7 +375,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 a7507c47..e39f9a5b 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 2f67faa7..86dc0d37 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() @@ -126,7 +126,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 @@ -149,7 +149,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) @@ -174,7 +174,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, @@ -201,7 +201,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 @@ -237,7 +237,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, @@ -262,7 +262,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() @@ -289,7 +289,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}" @@ -304,7 +304,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 @@ -327,7 +327,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 @@ -366,7 +366,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}" @@ -409,7 +409,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 ) @@ -443,7 +443,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 @@ -461,7 +461,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) ) @@ -516,7 +516,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"), @@ -543,7 +543,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}" @@ -562,7 +562,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" ) @@ -597,7 +597,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}", @@ -619,7 +619,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}" @@ -648,7 +648,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}", @@ -673,7 +673,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" ) @@ -698,7 +698,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" ) @@ -727,7 +727,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" ) @@ -754,7 +754,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", @@ -777,7 +777,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}" @@ -813,7 +813,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", @@ -853,7 +853,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", @@ -883,7 +883,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", @@ -915,7 +915,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", @@ -937,7 +937,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}" @@ -960,7 +960,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 2cacb0b9..0ac0f69f 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 b07bec91..abddeb06 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 @@ -1293,7 +1293,7 @@ async def test_job_submissions_agent_submitted__success( slurm_job_info="Fake slurm job info", ) - 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 @@ -1339,7 +1339,7 @@ async def test_job_submissions_agent_submitted__fails_if_status_is_not_CREATED( slurm_job_info="Fake slurm job info", ) - 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 @@ -1355,7 +1355,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( @@ -1399,7 +1399,7 @@ async def test_job_submissions_agent_submitted__fails_if_client_id_does_not_matc slurm_job_info="Fake slurm job info", ) - 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 @@ -1439,7 +1439,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", ) @@ -1489,7 +1489,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", ) @@ -1543,7 +1543,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 @@ -1580,7 +1580,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( @@ -1644,7 +1644,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( @@ -1695,7 +1695,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( @@ -1759,7 +1759,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", ) @@ -1800,7 +1800,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), @@ -1835,7 +1835,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( @@ -1873,7 +1873,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( @@ -1934,7 +1934,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}" @@ -1955,7 +1955,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 c871e025..7fbac651 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 +} From fbdd15f0b9e04a66669fdf870aaef5b488386c9c Mon Sep 17 00:00:00 2001 From: Tucker Beck Date: Mon, 15 Apr 2024 09:26:29 -0700 Subject: [PATCH 2/4] PENG-2159 reduced permissions sets for agent in composed --- jobbergate-composed/docker-compose.yml | 4 ++-- jobbergate-composed/etc/jobbergate-local.json | 10 +--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/jobbergate-composed/docker-compose.yml b/jobbergate-composed/docker-compose.yml index aed14667..e24ea67a 100644 --- a/jobbergate-composed/docker-compose.yml +++ b/jobbergate-composed/docker-compose.yml @@ -113,7 +113,7 @@ services: ports: - 5432:5432 healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] + test: ["CMD-SHELL", "pg_isready -U compose-db-user -d compose-db-name"] interval: 5s timeout: 5s retries: 5 @@ -132,7 +132,7 @@ services: ports: - 5433:5432 healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] + test: ["CMD-SHELL", "pg_isready -U test-user -d test-db && pg_isready -U test-user -d alt-test-db"] interval: 5s timeout: 5s retries: 5 diff --git a/jobbergate-composed/etc/jobbergate-local.json b/jobbergate-composed/etc/jobbergate-local.json index 7fbac651..4d999057 100755 --- a/jobbergate-composed/etc/jobbergate-local.json +++ b/jobbergate-composed/etc/jobbergate-local.json @@ -694,18 +694,10 @@ ], "clientRoles": { "local-slurm": [ - "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" + "jobbergate:job-submissions:update" ] }, "notBefore": 0, From 77c51d69f31503fa01ce314d1e6189001ed6ea5b Mon Sep 17 00:00:00 2001 From: Tucker Beck Date: Mon, 15 Apr 2024 09:38:36 -0700 Subject: [PATCH 3/4] PENG-2159 Fixed broken unit tests --- .../tests/apps/job_script_templates/test_routers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 86dc0d37..8aea6216 100644 --- a/jobbergate-api/tests/apps/job_script_templates/test_routers.py +++ b/jobbergate-api/tests/apps/job_script_templates/test_routers.py @@ -77,7 +77,7 @@ async def test_create_job_template__fails_if_name_is_empty( ) 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 == 422 @@ -99,7 +99,7 @@ async def test_create_job_template__coerces_empty_identifier_to_None( ) 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}" From bc2c200623cea084ce31b865a36a3726a679303e Mon Sep 17 00:00:00 2001 From: Tucker Beck Date: Tue, 23 Apr 2024 08:28:10 -0700 Subject: [PATCH 4/4] PENG-2159 Added cluster permissions --- .../jobbergate_api/apps/clusters/routers.py | 4 ++-- jobbergate-api/jobbergate_api/apps/permissions.py | 2 ++ jobbergate-api/tests/apps/clusters/test_routers.py | 12 ++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/jobbergate-api/jobbergate_api/apps/clusters/routers.py b/jobbergate-api/jobbergate_api/apps/clusters/routers.py index 24112cff..35d2946f 100644 --- a/jobbergate-api/jobbergate_api/apps/clusters/routers.py +++ b/jobbergate-api/jobbergate_api/apps/clusters/routers.py @@ -25,7 +25,7 @@ async def report_cluster_status( interval: int = Query(description="The interval in seconds between pings.", gt=0), secure_session: SecureSession = Depends( - secure_session(Permissions.JOB_SUBMISSIONS_EDIT, ensure_client_id=True) + secure_session(Permissions.CLUSTERS_UPDATE, ensure_client_id=True) ), ): """ @@ -52,7 +52,7 @@ async def report_cluster_status( response_model=Page[ClusterStatusView], ) async def get_cluster_status( - secure_session: SecureSession = Depends(secure_session(Permissions.JOB_SUBMISSIONS_VIEW, commit=False)), + secure_session: SecureSession = Depends(secure_session(Permissions.CLUSTERS_READ, commit=False)), ): """ Get the status of the cluster. diff --git a/jobbergate-api/jobbergate_api/apps/permissions.py b/jobbergate-api/jobbergate_api/apps/permissions.py index e39f9a5b..20ba2241 100644 --- a/jobbergate-api/jobbergate_api/apps/permissions.py +++ b/jobbergate-api/jobbergate_api/apps/permissions.py @@ -22,3 +22,5 @@ class Permissions(str, Enum): JOB_SUBMISSIONS_READ = "jobbergate:job-submissions:read" JOB_SUBMISSIONS_UPDATE = "jobbergate:job-submissions:update" JOB_SUBMISSIONS_DELETE = "jobbergate:job-submissions:delete" + CLUSTERS_READ = "jobbergate:clusters:read" + CLUSTERS_UPDATE = "jobbergate:clusters:update" diff --git a/jobbergate-api/tests/apps/clusters/test_routers.py b/jobbergate-api/tests/apps/clusters/test_routers.py index 1e91fcd3..2364b8ef 100644 --- a/jobbergate-api/tests/apps/clusters/test_routers.py +++ b/jobbergate-api/tests/apps/clusters/test_routers.py @@ -12,7 +12,7 @@ async def test_report_cluster_status__create(self, client, inject_security_heade client_id = "dummy-client" inject_security_header( "who@cares.com", - Permissions.JOB_SUBMISSIONS_EDIT, + Permissions.CLUSTERS_UPDATE, client_id=client_id, ) @@ -34,7 +34,7 @@ async def test_report_cluster_status__update(self, client, inject_security_heade client_id = "dummy-client" inject_security_header( "who@cares.com", - Permissions.JOB_SUBMISSIONS_EDIT, + Permissions.CLUSTERS_UPDATE, client_id=client_id, ) @@ -64,7 +64,7 @@ async def test_report__invalid_interval_raises_error( client_id = "dummy-client" inject_security_header( "who@cares.com", - Permissions.JOB_SUBMISSIONS_EDIT, + Permissions.CLUSTERS_UPDATE, client_id=client_id, ) @@ -74,7 +74,7 @@ async def test_report__invalid_interval_raises_error( async def test_report_cluster_status__no_client_id(self, client, inject_security_header): - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_EDIT) + inject_security_header("who@cares.com", Permissions.CLUSTERS_UPDATE) response = await client.put("/jobbergate/clusters/status", params={"interval": 60}) @@ -95,7 +95,7 @@ async def test_get_cluster_status__empty( self, client, inject_security_header, unpack_response, synth_session ): - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("who@cares.com", Permissions.CLUSTERS_READ) response = await client.get("/jobbergate/clusters/status") assert unpack_response(response, check_total=0, check_page=1, check_pages=0) == [] @@ -110,7 +110,7 @@ async def test_get_cluster_status__list( ClusterStatus(client_id="client-3", interval=30, last_reported=pendulum.datetime(2022, 1, 1)), ] - inject_security_header("who@cares.com", Permissions.JOB_SUBMISSIONS_VIEW) + inject_security_header("who@cares.com", Permissions.CLUSTERS_READ) with pendulum.test(pendulum.datetime(2023, 1, 1)): synth_session.add_all(statuses)