From b5515c8b17f953fd07298cee0d1da6f9a3d32823 Mon Sep 17 00:00:00 2001 From: Matthew Pritchard <46708056+MattPrit@users.noreply.github.com> Date: Tue, 26 May 2026 15:23:06 +0100 Subject: [PATCH 1/3] feat: add ulims authz policy --- policy/diamond/policy/ulims/ulims.rego | 115 +++++++++++++++++ policy/diamond/policy/ulims/ulims_test.rego | 134 ++++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 policy/diamond/policy/ulims/ulims.rego create mode 100644 policy/diamond/policy/ulims/ulims_test.rego diff --git a/policy/diamond/policy/ulims/ulims.rego b/policy/diamond/policy/ulims/ulims.rego new file mode 100644 index 0000000..4c9f909 --- /dev/null +++ b/policy/diamond/policy/ulims/ulims.rego @@ -0,0 +1,115 @@ +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 v if { + "*" in user_sessions + some v in input.instrument_sessions +} + +filter_sessions contains v if { + some v in input.instrument_sessions + proposal_number := format_int(v[0], 10) + session_number := format_int(v[1], 10) + format_int(data.diamond.data.proposals[proposal_number].sessions[session_number], 10) in user_sessions +} diff --git a/policy/diamond/policy/ulims/ulims_test.rego b/policy/diamond/policy/ulims/ulims_test.rego new file mode 100644 index 0000000..1c8b9a7 --- /dev/null +++ b/policy/diamond/policy/ulims/ulims_test.rego @@ -0,0 +1,134 @@ +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 data.diamond.policy.token as {"claims": {"fedid": "carol"}} + with input.instrument_sessions as [[1, 1], [1, 2], [2, 1], [2, 2]] +} + +test_filter_sessions_beamline_admin if { + ulims.filter_sessions == {[1, 2], [2, 1], [2, 2]} with data.diamond.data as diamond_data + with data.diamond.policy.token as {"claims": {"fedid": "bob"}} + with input.instrument_sessions as [[1, 1], [1, 2], [2, 1], [2, 2]] +} + +test_filter_sessions_for_non_admin_1 if { + ulims.filter_sessions == {[1, 1], [1, 2]} with data.diamond.data as diamond_data + with data.diamond.policy.token as {"claims": {"fedid": "alice"}} + with input.instrument_sessions as [[1, 1], [1, 2], [2, 1], [2, 2]] +} + +test_filter_sessions_for_non_admin_2 if { + ulims.filter_sessions == set() with data.diamond.data as diamond_data + with data.diamond.policy.token as {"claims": {"fedid": "oscar"}} + with input.instrument_sessions as [[1, 1], [1, 2], [2, 1], [2, 2]] +} + +test_filter_sessions_service_account if { + ulims.filter_sessions == {[1, 1]} with data.diamond.data as diamond_data + with data.diamond.policy.token as {"claims": {"beamline": "i03"}} + with input.instrument_sessions as [[1, 1], [1, 2], [2, 1], [2, 2]] +} From 2afbdb17c6437c0d13bf429c274510b70367bfb7 Mon Sep 17 00:00:00 2001 From: Matthew Pritchard <46708056+MattPrit@users.noreply.github.com> Date: Tue, 26 May 2026 15:43:23 +0100 Subject: [PATCH 2/3] fix: ulims lint errors --- policy/diamond/policy/ulims/ulims_test.rego | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/policy/diamond/policy/ulims/ulims_test.rego b/policy/diamond/policy/ulims/ulims_test.rego index 1c8b9a7..dba615d 100644 --- a/policy/diamond/policy/ulims/ulims_test.rego +++ b/policy/diamond/policy/ulims/ulims_test.rego @@ -1,4 +1,4 @@ -package diamond.policy.ulims.test +package diamond.policy.ulims_test import data.diamond.policy.ulims import rego.v1 @@ -99,7 +99,11 @@ test_session_restrictions_for_non_admin_2 if { } test_session_restrictions_service_account if { - ulims.session_restrictions == [{"beamline": "i03", "proposal_number": 1, "visit_number": 1}] with data.diamond.data as diamond_data + 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"} } From 6e60748074cd922c198f89290a0efdebbc7d0ac0 Mon Sep 17 00:00:00 2001 From: Matthew Pritchard <46708056+MattPrit@users.noreply.github.com> Date: Wed, 27 May 2026 11:35:22 +0100 Subject: [PATCH 3/3] feat: add ulims.filter_instruments rule --- policy/diamond/policy/ulims/ulims.rego | 36 +++++++++++++++++---- policy/diamond/policy/ulims/ulims_test.rego | 34 ++++++++++++++++--- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/policy/diamond/policy/ulims/ulims.rego b/policy/diamond/policy/ulims/ulims.rego index 4c9f909..6b7f23d 100644 --- a/policy/diamond/policy/ulims/ulims.rego +++ b/policy/diamond/policy/ulims/ulims.rego @@ -102,14 +102,38 @@ session_restrictions := [data.diamond.data.sessions[session_id] | some session_i # - `input.token`, a JWT # - `input.instrument_sessions`, an array representing a list of instrument sessions, [(proposal, visit), ...] # entrypoint: true -filter_sessions contains v if { +filter_sessions contains session if { "*" in user_sessions - some v in input.instrument_sessions + some session in input.instrument_sessions } -filter_sessions contains v if { - some v in input.instrument_sessions - proposal_number := format_int(v[0], 10) - session_number := format_int(v[1], 10) +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 index dba615d..d5ec3e8 100644 --- a/policy/diamond/policy/ulims/ulims_test.rego +++ b/policy/diamond/policy/ulims/ulims_test.rego @@ -109,30 +109,54 @@ test_session_restrictions_service_account if { 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 data.diamond.policy.token as {"claims": {"fedid": "carol"}} 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 data.diamond.policy.token as {"claims": {"fedid": "bob"}} 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 data.diamond.policy.token as {"claims": {"fedid": "alice"}} 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 data.diamond.policy.token as {"claims": {"fedid": "oscar"}} 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 data.diamond.policy.token as {"claims": {"beamline": "i03"}} 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"}} }