diff --git a/policy/diamond/policy/blueapi/blueapi.rego b/policy/diamond/policy/blueapi/blueapi.rego index bf2df8a7..69b8757d 100644 --- a/policy/diamond/policy/blueapi/blueapi.rego +++ b/policy/diamond/policy/blueapi/blueapi.rego @@ -1,12 +1,65 @@ package diamond.policy.blueapi +import data.diamond.policy.admin +import data.diamond.policy.session import data.diamond.policy.token + import rego.v1 -default tiled_service_account_for_beamline := false +_session := data.diamond.data.proposals[format_int(input.proposal, 10)].sessions[format_int(input.visit, 10)] + +# Returns the session ID if the subject has write permissions for the +# specific beamline, visit and proposal requested in the input. +user_session := format_int(_session, 10) if { + session.write_to_beamline_visit + _session +} + +# Check if user should be able to submit tasks only if they're on +# the same instrument as the instrument session in question. + +default post_task := false + +post_task if { + session.write_to_beamline_visit +} + +default delete_task := false + +delete_task if { + data.diamond.data.tasks[input.task_id].created_by == token.claims.fedid +} + +delete_task if { + admin.is_admin(token.claims.fedid) +} + +default fetch_task := false + +fetch_task if { + data.diamond.data.tasks[input.task_id].created_by == token.claims.fedid +} + +fetch_task if { + admin.is_admin(token.claims.fedid) +} + +fetch_tasks contains task_ids if { + some task_ids + data.diamond.data.tasks[task_ids].created_by == token.claims.fedid +} + +fetch_tasks contains task_ids if { + admin.is_admin(token.claims.fedid) + some task_ids, _ in data.diamond.data.tasks +} + +default put_worker_state_abort := false + +put_worker_state_abort if { + data.diamond.data.tasks[input.task_id].created_by == token.claims.fedid +} -tiled_service_account_for_beamline if { - input.beamline == token.claims.beamline - "tiled-writer" in token.claims.aud - not token.claims.fedid +put_worker_state_abort if { + admin.is_admin(token.claims.fedid) } diff --git a/policy/diamond/policy/blueapi/blueapi_test.rego b/policy/diamond/policy/blueapi/blueapi_test.rego index 73e94a43..037d06c6 100644 --- a/policy/diamond/policy/blueapi/blueapi_test.rego +++ b/policy/diamond/policy/blueapi/blueapi_test.rego @@ -3,22 +3,182 @@ package diamond.policy.blueapi_test import data.diamond.policy.blueapi import rego.v1 -test_service_account_if_beamline_matches if { - blueapi.tiled_service_account_for_beamline with input as {"beamline": "i22"} - with data.diamond.policy.token.claims as {"beamline": "i22", "aud": ["tiled-writer"]} +diamond_data := { + "subjects": { + "alice": { + "permissions": [], + "proposals": [1], + "sessions": [], + }, + "bob": { + "permissions": ["b07_admin"], + "proposals": [], + "sessions": [11], + }, + "carol": { + "permissions": ["super_admin"], + "proposals": [], + "sessions": [], + }, + "oscar": { + "permissions": [], + "proposals": [], + "sessions": [], + }, + }, + "sessions": { + "11": { + "beamline": "i03", + "proposal_number": 1, + "visit_number": 1, + }, + "12": { + "beamline": "b07", + "proposal_number": 1, + "visit_number": 2, + }, + }, + "proposals": {"1": {"sessions": { + "1": 11, + "2": 12, + }}}, + "tasks": { + "1": { + "task_id": "1", + "created_by": "alice", + "session_id": "456", + }, + "2": { + "task_id": "2", + "created_by": "bob", + "session_id": "457", + }, + "3": { + "task_id": "3", + "created_by": "oscar", + "session_id": "458", + }, + "4": { + "task_id": "4", + "created_by": "oscar", + "session_id": "459", + }, + }, + "beamlines": {"i03": {"sessions": [11]}, "b07": {"sessions": [12]}}, + "admin": {"b07_admin": ["b07"]}, } -test_not_service_account_if_beamline_mismatch if { - not blueapi.tiled_service_account_for_beamline with input as {"beamline": "b21"} - with data.diamond.policy.token.claims as {"beamline": "i22", "aud": ["tiled-writer"]} +test_user_session_not_allowed if { + not blueapi.user_session with data.diamond.data as diamond_data + with input as {"proposal": 1, "visit": 1, "beamline": "i03"} + with data.diamond.policy.token.claims as {"fedid": "oscar"} } -test_not_service_account_if_missing_aud if { - not blueapi.tiled_service_account_for_beamline with input as {"beamline": "i22"} - with data.diamond.policy.token.claims as {"beamline": "i22", "aud": ["blueapiCli"]} +test_user_session_allow if { + blueapi.user_session with data.diamond.data as diamond_data + with input as {"proposal": 1, "visit": 1, "beamline": "i03"} + with data.diamond.policy.token.claims as {"fedid": "bob"} } -test_not_service_account_if_fedid_present if { - not blueapi.tiled_service_account_for_beamline with input as {"beamline": "i22"} - with data.diamond.policy.token.claims as {"beamline": "i22", "aud": ["tiled-writer"], "fedid": "abc12345"} +# b07_admin user can access a b07 session via their role, not direct session membership +test_user_session_allow_for_beamline_admin_via_role if { + blueapi.user_session with data.diamond.data as diamond_data + with input as {"proposal": 1, "visit": 2, "beamline": "b07"} + with data.diamond.policy.token.claims as {"fedid": "bob"} +} + +# Instrument session has to match instrument of blueapi instance test +test_user_session_not_allowed_if_instrument_session_doesnt_match_blueapi_instance if { + not blueapi.user_session with data.diamond.data as diamond_data + with input as {"proposal": 1, "visit": 1, "beamline": "b07"} + with data.diamond.policy.token.claims as {"fedid": "bob"} +} + +# POST /tasks denied if user not on instrument session test +test_post_tasks_not_allowed_if_user_not_on_instrument_session if { + not blueapi.post_task with data.diamond.data as diamond_data + with input as {"proposal": 1, "visit": 1, "beamline": "i03"} + with data.diamond.policy.token.claims as {"fedid": "oscar"} +} + +# POST /tasks allowed if user on instrument session test +test_post_tasks_allowed_if_user_on_instrument_session if { + blueapi.post_task with data.diamond.data as diamond_data + with input as {"proposal": 1, "visit": 1, "beamline": "i03"} + with data.diamond.policy.token.claims as {"fedid": "bob"} +} + +# DELETE /task denied if fed_id doesn't match task owner test +test_delete_task_not_allowed_if_fed_id_doesnt_match_task_owner if { + not blueapi.delete_task with data.diamond.data as diamond_data + with input as {"task_id": "1"} + with data.diamond.policy.token.claims as {"fedid": "oscar"} +} + +# DELETE /task allowed if fed_id matches task owner test +test_delete_task_allowed_if_fed_id_matches_task_owner if { + blueapi.delete_task with data.diamond.data as diamond_data + with input as {"task_id": "1"} + with data.diamond.policy.token.claims as {"fedid": "alice"} +} + +# GET /tasks returns only tasks submitted by requesting user test +test_get_tasks_returns_only_tasks_submitted_by_requesting_user if { + blueapi.fetch_tasks == {"3", "4"} with data.diamond.data as diamond_data + with data.diamond.policy.token.claims as {"fedid": "oscar"} +} + +# GET task/{task_id} denied if tasks not submitted by requesting user test +test_get_task_not_allowed_if_task_not_submitted_by_requesting_user if { + not blueapi.fetch_task with data.diamond.data as diamond_data + with input as {"task_id": "1"} + with data.diamond.policy.token.claims as {"fedid": "oscar"} +} + +# GET task/{task_id} allowed if task submitted by requesting user test +test_get_task_allowed_if_task_submitted_by_requesting_user if { + blueapi.fetch_task with data.diamond.data as diamond_data + with input as {"task_id": "3"} + with data.diamond.policy.token.claims as {"fedid": "oscar"} +} + +# PUT /worker/state abort denied if not task creator test +test_put_worker_state_abort_not_allowed_if_not_task_creator if { + not blueapi.put_worker_state_abort with data.diamond.data as diamond_data + with input as {"task_id": "1"} + with data.diamond.policy.token.claims as {"fedid": "oscar"} +} + +# PUT /worker/state abort allowed if task creator test +test_put_worker_state_abort_allowed_if_task_creator if { + blueapi.put_worker_state_abort with data.diamond.data as diamond_data + with input as {"task_id": "1"} + with data.diamond.policy.token.claims as {"fedid": "alice"} +} + +# DELETE /task allowed for admin regardless of task ownership +test_delete_task_allowed_for_admin if { + blueapi.delete_task with data.diamond.data as diamond_data + with input as {"task_id": "1"} + with data.diamond.policy.token.claims as {"fedid": "carol"} +} + +# GET /task/{task_id} allowed for admin regardless of task ownership +test_get_task_allowed_for_admin if { + blueapi.fetch_task with data.diamond.data as diamond_data + with input as {"task_id": "1"} + with data.diamond.policy.token.claims as {"fedid": "carol"} +} + +# GET /tasks returns all tasks for admin +test_get_tasks_returns_all_tasks_for_admin if { + blueapi.fetch_tasks == {"1", "2", "3", "4"} with data.diamond.data as diamond_data + with data.diamond.policy.token.claims as {"fedid": "carol"} +} + +# PUT /worker/state abort allowed for admin regardless of task ownership +test_put_worker_state_abort_allowed_for_admin if { + blueapi.put_worker_state_abort with data.diamond.data as diamond_data + with input as {"task_id": "1"} + with data.diamond.policy.token.claims as {"fedid": "carol"} }