diff --git a/policy/diamond/policy/ulims/ulims.rego b/policy/diamond/policy/ulims/ulims.rego new file mode 100644 index 0000000..6b7f23d --- /dev/null +++ b/policy/diamond/policy/ulims/ulims.rego @@ -0,0 +1,139 @@ +package diamond.policy.ulims + +# import data.diamond.data +import data.diamond.policy.admin +import data.diamond.policy.session +import data.diamond.policy.token +import rego.v1 + +# --- Start section copied from tiled.rego --- + +subject := data.diamond.data.subjects[token.claims.fedid] + +# METADATA +# title: Beamlines +# description: | +# Identifies all beamlines the subject is authorized to access +# based on their assigned permissions. +beamlines contains beamline if { + token.claims.fedid + not admin.is_admin(token.claims.fedid) + some p in subject.permissions + some beamline in object.get(data.diamond.data.admin, p, []) +} + +# Aggregates all session IDs the subject is authorized to view. +# Admins receive a wildcard "*" granting access to all sessions. +# Regular users gain session access through three pathways: +# 1. Direct session membership +# 2. Access via beamline-level permissions +# 3. Access via proposal-level permissions +user_sessions contains "*" if { + subject + admin.is_admin(token.claims.fedid) +} + +user_sessions contains format_int(session, 10) if { + subject + not admin.is_admin(token.claims.fedid) + some session in subject.sessions +} + +user_sessions contains format_int(session, 10) if { + subject + not admin.is_admin(token.claims.fedid) + some beamline in beamlines + some session in data.diamond.data.beamlines[beamline].sessions +} + +user_sessions contains format_int(session, 10) if { + subject + not admin.is_admin(token.claims.fedid) + some p in subject.proposals + some i in data.diamond.data.proposals[format_int(p, 10)] + some session in i +} + +# service account check +user_sessions contains format_int(session, 10) if { + not subject + some session in data.diamond.data.beamlines[token.claims.beamline].sessions +} + +# --- End section copied from tiled.rego --- + +# METADATA +# description: Allow verified tokens +# entrypoint: true +main := {"allow": allow} + +default allow := false + +allow if { + token.verified[0] +} + +# METADATA +# title: Session Restrictions +# description: | +# Return the instrument sessions the current user is allowed to see, or null if the user is an admin +# Requires: +# - `input.token`, a JWT +# entrypoint: true +default session_restrictions := [] + +session_restrictions := null if { + admin.is_admin(token.claims.fedid) +} + +session_restrictions := [data.diamond.data.sessions[session_id] | some session_id in user_sessions] if { + not admin.is_admin(token.claims.fedid) +} + +session_restrictions := [data.diamond.data.sessions[session_id] | some session_id in user_sessions] if { + not token.claims.fedid +} + +# METADATA +# title: Filter sessions +# description: | +# Filter a provided list of instrument sessions, returning just those that the user has access to +# Requires: +# - `input.token`, a JWT +# - `input.instrument_sessions`, an array representing a list of instrument sessions, [(proposal, visit), ...] +# entrypoint: true +filter_sessions contains session if { + "*" in user_sessions + some session in input.instrument_sessions +} + +filter_sessions contains session if { + some session in input.instrument_sessions + proposal_number := format_int(session[0], 10) + session_number := format_int(session[1], 10) + format_int(data.diamond.data.proposals[proposal_number].sessions[session_number], 10) in user_sessions +} + +# METADATA +# title: Filter instruments +# description: | +# Filter a provided list of instruments, returning just those that the user has access to +# Requires: +# - `input.token`, a JWT +# - `input.instruments`, an array of strings representing a list of instruments +# entrypoint: true +filter_instruments contains instrument if { + some instrument in input.instruments + instrument in beamlines +} + +filter_instruments contains instrument if { + admin.is_admin(token.claims.fedid) + some instrument in input.instruments +} + +filter_instruments contains instrument if { + token.claims.beamline + some instrument in input.instruments + instrument == token.claims.beamline +} diff --git a/policy/diamond/policy/ulims/ulims_test.rego b/policy/diamond/policy/ulims/ulims_test.rego new file mode 100644 index 0000000..d5ec3e8 --- /dev/null +++ b/policy/diamond/policy/ulims/ulims_test.rego @@ -0,0 +1,162 @@ +package diamond.policy.ulims_test + +import data.diamond.policy.ulims +import rego.v1 + +diamond_data := { + "subjects": { + "alice": { + "permissions": [], + "proposals": [1], + "sessions": [], + }, + "bob": { + "permissions": ["b07_admin"], + "proposals": [], + "sessions": [], + }, + "carol": { + "permissions": ["super_admin"], + "proposals": [], + "sessions": [], + }, + "desmond": { + "permissions": [], + "proposals": [2], + "sessions": [13], + }, + "edna": { + "permissions": [], + "proposals": [2], + "sessions": [13, 14], + }, + "oscar": { + "permissions": [], + "proposals": [], + "sessions": [], + }, + }, + "sessions": { + "11": { + "beamline": "i03", + "proposal_number": 1, + "visit_number": 1, + }, + "12": { + "beamline": "b07", + "proposal_number": 1, + "visit_number": 2, + }, + "13": { + "beamline": "b07", + "proposal_number": 2, + "visit_number": 1, + }, + "14": { + "beamline": "b07", + "proposal_number": 2, + "visit_number": 2, + }, + }, + "proposals": { + "1": {"sessions": { + "1": 11, + "2": 12, + }}, + "2": {"sessions": { + "1": 13, + "2": 14, + }}, + }, + "beamlines": {"i03": {"sessions": [11]}, "b07": {"sessions": [12, 13, 14]}}, + "admin": {"b07_admin": ["b07"]}, +} + +test_session_restrictions_for_admin if { + ulims.session_restrictions == null with data.diamond.data as diamond_data + with data.diamond.policy.token as {"claims": {"fedid": "carol"}} +} + +test_session_restrictions_for_non_admin_1 if { + ulims.session_restrictions == [ + { + "beamline": "i03", + "proposal_number": 1, + "visit_number": 1, + }, + { + "beamline": "b07", + "proposal_number": 1, + "visit_number": 2, + }, + ] with data.diamond.data as diamond_data + with data.diamond.policy.token as {"claims": {"fedid": "alice"}} +} + +test_session_restrictions_for_non_admin_2 if { + ulims.session_restrictions == [] with data.diamond.data as diamond_data + with data.diamond.policy.token as {"claims": {"fedid": "oscar"}} +} + +test_session_restrictions_service_account if { + ulims.session_restrictions == [{ + "beamline": "i03", + "proposal_number": 1, + "visit_number": 1, + }] with data.diamond.data as diamond_data + with data.diamond.policy.token.claims as {"beamline": "i03"} +} + +test_filter_sessions_for_admin if { + ulims.filter_sessions == {[1, 1], [1, 2], [2, 1], [2, 2]} with data.diamond.data as diamond_data + with input.instrument_sessions as [[1, 1], [1, 2], [2, 1], [2, 2]] + with data.diamond.policy.token as {"claims": {"fedid": "carol"}} +} + +test_filter_sessions_beamline_admin if { + ulims.filter_sessions == {[1, 2], [2, 1], [2, 2]} with data.diamond.data as diamond_data + with input.instrument_sessions as [[1, 1], [1, 2], [2, 1], [2, 2]] + with data.diamond.policy.token as {"claims": {"fedid": "bob"}} +} + +test_filter_sessions_for_non_admin_1 if { + ulims.filter_sessions == {[1, 1], [1, 2]} with data.diamond.data as diamond_data + with input.instrument_sessions as [[1, 1], [1, 2], [2, 1], [2, 2]] + with data.diamond.policy.token as {"claims": {"fedid": "alice"}} +} + +test_filter_sessions_for_non_admin_2 if { + ulims.filter_sessions == set() with data.diamond.data as diamond_data + with input.instrument_sessions as [[1, 1], [1, 2], [2, 1], [2, 2]] + with data.diamond.policy.token as {"claims": {"fedid": "oscar"}} +} + +test_filter_sessions_service_account if { + ulims.filter_sessions == {[1, 1]} with data.diamond.data as diamond_data + with input.instrument_sessions as [[1, 1], [1, 2], [2, 1], [2, 2]] + with data.diamond.policy.token as {"claims": {"beamline": "i03"}} +} + +test_filter_instruments_user if { + ulims.filter_instruments == set() with data.diamond.data as diamond_data + with input.instruments as ["i03", "b07"] + with data.diamond.policy.token as {"claims": {"fedid": "alice"}} +} + +test_filter_instruments_beamline_admin if { + ulims.filter_instruments == {"b07"} with data.diamond.data as diamond_data + with input.instruments as ["i03", "b07"] + with data.diamond.policy.token as {"claims": {"fedid": "bob"}} +} + +test_filter_instruments_super_admin if { + ulims.filter_instruments == {"i03", "b07"} with data.diamond.data as diamond_data + with input.instruments as ["i03", "b07"] + with data.diamond.policy.token as {"claims": {"fedid": "carol"}} +} + +test_filter_instruments_service_account if { + ulims.filter_instruments == {"i03"} with data.diamond.data as diamond_data + with input.instruments as ["i03", "b07"] + with data.diamond.policy.token as {"claims": {"beamline": "i03"}} +}