From daa0f522825a7a4932242579adb8ac337ad34e5f Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Tue, 3 Feb 2026 10:53:55 +0000 Subject: [PATCH 1/5] feat: update tiled policy --- policy/diamond/policy/tiled/tiled.rego | 64 ++++++++++++++++++--- policy/diamond/policy/tiled/tiled_test.rego | 53 +++++++++-------- 2 files changed, 86 insertions(+), 31 deletions(-) diff --git a/policy/diamond/policy/tiled/tiled.rego b/policy/diamond/policy/tiled/tiled.rego index 3c94495..be7574d 100644 --- a/policy/diamond/policy/tiled/tiled.rego +++ b/policy/diamond/policy/tiled/tiled.rego @@ -1,5 +1,6 @@ package diamond.policy.tiled +import data.diamond.policy.admin import data.diamond.policy.session import data.diamond.policy.token import rego.v1 @@ -30,15 +31,64 @@ scopes_for(claims) := read_scopes if { not "azp" in object.keys(claims) } +# Assign read & write scopes to blueapi clients +# defaults to read-only scopes default scopes := set() scopes := scopes_for(token.claims) -user_sessions contains user_session if { - some i in data.diamond.data.sessions - session.access_session(token.claims.fedid, i.proposal_number, i.visit_number) - user_session := sprintf( - `{"proposal": %d, "visit": %d, "beamline": "%s"}`, - [i.proposal_number, i.visit_number, i.beamline], - ) +# Returns the session ID if the subject has write permissions for the +# specific beamline, visit and proposal requested in the input. +user_session := to_number(value) if { + session.write_to_beamline_visit + value := data.diamond.data.proposals[format_int(input.proposal, 10)].sessions[format_int(input.visit, 10)] +} + +# Validates if the subject has permission to modify +# the specific session in the input. +default modify_session := false + +modify_session := session.access_session( + token.claims.fedid, + data.diamond.data.sessions[input.session].proposal_number, + data.diamond.data.sessions[input.session].visit_number, +) + +subject := data.diamond.data.subjects[token.claims.fedid] + +# Identifies all beamlines the subject is authorized to access +# based on their assigned permissions. +beamlines contains beamline if { + 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 { + admin.is_admin(token.claims.fedid) +} + +user_sessions contains to_number(session) if { + not admin.is_admin(token.claims.fedid) + some session in subject.sessions +} + +user_sessions contains to_number(session) if { + not admin.is_admin(token.claims.fedid) + some beamline in beamlines + some session in data.diamond.data.beamlines[beamline].sessions +} + +user_sessions contains to_number(session) if { + 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 } diff --git a/policy/diamond/policy/tiled/tiled_test.rego b/policy/diamond/policy/tiled/tiled_test.rego index 14a4666..d7188ec 100644 --- a/policy/diamond/policy/tiled/tiled_test.rego +++ b/policy/diamond/policy/tiled/tiled_test.rego @@ -96,33 +96,38 @@ diamond_data := { test_user_session_tags if { tiled.user_sessions == set() with data.diamond.data as diamond_data with data.diamond.policy.token.claims as {"fedid": "oscar"} - tiled.user_sessions == { - `{"proposal": 1, "visit": 2, "beamline": "b07"}`, - `{"proposal": 1, "visit": 1, "beamline": "i03"}`, - } with data.diamond.data as diamond_data + tiled.user_sessions == {11, 12} with data.diamond.data as diamond_data with data.diamond.policy.token.claims as {"fedid": "alice"} - tiled.user_sessions == { - `{"proposal": 1, "visit": 2, "beamline": "b07"}`, - `{"proposal": 1, "visit": 1, "beamline": "i03"}`, - `{"proposal": 2, "visit": 1, "beamline": "b07"}`, - `{"proposal": 2, "visit": 2, "beamline": "b07"}`, - } with data.diamond.data as diamond_data + tiled.user_sessions == {11, 12, 13, 14} with data.diamond.data as diamond_data with data.diamond.policy.token.claims as {"fedid": "bob"} - tiled.user_sessions == { - `{"proposal": 1, "visit": 2, "beamline": "b07"}`, - `{"proposal": 1, "visit": 1, "beamline": "i03"}`, - `{"proposal": 2, "visit": 1, "beamline": "b07"}`, - `{"proposal": 2, "visit": 2, "beamline": "b07"}`, - } with data.diamond.data as diamond_data + tiled.user_sessions == {"*"} with data.diamond.data as diamond_data with data.diamond.policy.token.claims as {"fedid": "carol"} - tiled.user_sessions == { - `{"proposal": 2, "visit": 1, "beamline": "b07"}`, - `{"proposal": 2, "visit": 2, "beamline": "b07"}`, - } with data.diamond.data as diamond_data + tiled.user_sessions == {13, 14} with data.diamond.data as diamond_data with data.diamond.policy.token.claims as {"fedid": "desmond"} - tiled.user_sessions == { - `{"proposal": 2, "visit": 1, "beamline": "b07"}`, - `{"proposal": 2, "visit": 2, "beamline": "b07"}`, - } with data.diamond.data as diamond_data + tiled.user_sessions == {13, 14} with data.diamond.data as diamond_data with data.diamond.policy.token.claims as {"fedid": "edna"} } + +test_user_session_allow if { + tiled.user_session == 11 with data.diamond.data as diamond_data + with input as {"beamline": "i03", "proposal": 1, "visit": 1} + with data.diamond.policy.token.claims as {"fedid": "carol"} +} + +test_user_session_not_allowed if { + not tiled.user_session with data.diamond.data as diamond_data + with input as {"beamline": "i03", "proposal": 1, "visit": 1} + with data.diamond.policy.token.claims as {"fedid": "oscar"} +} + +test_not_modify_session if { + not tiled.modify_session with data.diamond.data as diamond_data + with input as {"session": "13"} + with data.diamond.policy.token.claims as {"fedid": "alice"} +} + +test_modify_session if { + tiled.modify_session with data.diamond.data as diamond_data + with input as {"session": "11"} + with data.diamond.policy.token.claims as {"fedid": "alice"} +} From 47e8d9ca34efdb92cc3995ad16ea8a6501074825 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Wed, 18 Feb 2026 09:48:08 +0000 Subject: [PATCH 2/5] feat: update policy for service accounts --- policy/diamond/policy/tiled/tiled.rego | 67 ++++++++----- policy/diamond/policy/tiled/tiled_test.rego | 101 ++++++++++++++++++-- 2 files changed, 135 insertions(+), 33 deletions(-) diff --git a/policy/diamond/policy/tiled/tiled.rego b/policy/diamond/policy/tiled/tiled.rego index be7574d..152a090 100644 --- a/policy/diamond/policy/tiled/tiled.rego +++ b/policy/diamond/policy/tiled/tiled.rego @@ -5,56 +5,77 @@ import data.diamond.policy.session import data.diamond.policy.token import rego.v1 -read_scopes := { +# Assign read & write scopes to clients with tiled-writer audience +# defaults to read-only scopes +default scopes := { "read:metadata", "read:data", } -write_scopes := { +scopes := { + "read:metadata", + "read:data", "write:metadata", "write:data", "create:node", "register", +} if { + "tiled-writer" in token.claims.aud } -scopes_for(claims) := read_scopes | write_scopes if { - "azp" in object.keys(claims) - endswith(claims.azp, "-blueapi") -} +# Returns the session ID if the subject has write permissions for the +# specific beamline, visit and proposal requested in the input. +_session := data.diamond.data.proposals[format_int(input.proposal, 10)].sessions[format_int(input.visit, 10)] -scopes_for(claims) := read_scopes if { - "azp" in object.keys(claims) - not endswith(claims.azp, "-blueapi") +user_session := to_number(_session) if { + session.write_to_beamline_visit + _session } -scopes_for(claims) := read_scopes if { - not "azp" in object.keys(claims) -} +# Service account will need either proposal or session -# Assign read & write scopes to blueapi clients -# defaults to read-only scopes -default scopes := set() +user_session := to_number(_session) if { + input.proposal in token.claims.subject.proposals +} -scopes := scopes_for(token.claims) +user_session := to_number(_session) if { + _session in token.claims.subject.sessions +} -# Returns the session ID if the subject has write permissions for the -# specific beamline, visit and proposal requested in the input. -user_session := to_number(value) if { - session.write_to_beamline_visit - value := data.diamond.data.proposals[format_int(input.proposal, 10)].sessions[format_int(input.visit, 10)] +user_session := to_number(_session) if { + input.beamline in beamlines + input.beamline == session.beamline_for(input.proposal, input.visit) + _session in data.diamond.data.beamlines[input.beamline].sessions } # Validates if the subject has permission to modify # the specific session in the input. default modify_session := false -modify_session := session.access_session( +modify_session if session.access_session( token.claims.fedid, data.diamond.data.sessions[input.session].proposal_number, data.diamond.data.sessions[input.session].visit_number, ) -subject := data.diamond.data.subjects[token.claims.fedid] +modify_session if { + data.diamond.data.sessions[input.session].proposal_number in token.claims.subject.proposals +} + +modify_session if { + to_number(input.session) in token.claims.subject.sessions +} + +modify_session if { + session.beamline_for( + data.diamond.data.sessions[input.session].proposal_number, + data.diamond.data.sessions[input.session].visit_number, + ) in beamlines +} + +subject := data.diamond.data.subjects[token.claims.fedid] if token.claims.fedid + +else := token.claims.subject if token.claims.subject # Identifies all beamlines the subject is authorized to access # based on their assigned permissions. diff --git a/policy/diamond/policy/tiled/tiled_test.rego b/policy/diamond/policy/tiled/tiled_test.rego index d7188ec..bdf7ff1 100644 --- a/policy/diamond/policy/tiled/tiled_test.rego +++ b/policy/diamond/policy/tiled/tiled_test.rego @@ -3,17 +3,14 @@ package diamond.policy.tiled_test import data.diamond.policy.tiled import rego.v1 -test_default_no_scopes if { - tiled.scopes == set() -} - -test_wrong_azp_read_scopes if { - tiled.scopes == tiled.read_scopes with data.diamond.policy.token.claims as {} - tiled.scopes == tiled.read_scopes with data.diamond.policy.token.claims as {"sub": "foo"} - tiled.scopes == tiled.read_scopes with data.diamond.policy.token.claims as {"azp": "foo"} +test_read_scopes if { + tiled.scopes == { + "read:metadata", + "read:data", + } with data.diamond.policy.token.claims as {} } -test_blueapi_given_write_scopes if { +test_tiled_writer_given_write_scopes if { tiled.scopes == { "read:metadata", "read:data", @@ -21,7 +18,7 @@ test_blueapi_given_write_scopes if { "write:data", "create:node", "register", - } with data.diamond.policy.token.claims as {"azp": "foo-blueapi"} + } with data.diamond.policy.token.claims as {"aud": ["tiled-writer"]} } diamond_data := { @@ -131,3 +128,87 @@ test_modify_session if { with input as {"session": "11"} with data.diamond.policy.token.claims as {"fedid": "alice"} } + +# Service account tests + +test_user_session_allow_service_account_on_proposal if { + tiled.user_session == 11 with data.diamond.data as diamond_data + with input as {"beamline": "i03", "proposal": 1, "visit": 1} + with data.diamond.policy.token.claims as {"subject": {"proposals": [1], "sessions": [], "permissions": []}} +} + +test_user_session_allow_service_account_on_session if { + tiled.user_session == 11 with data.diamond.data as diamond_data + with input as {"beamline": "i03", "proposal": 1, "visit": 1} + with data.diamond.policy.token.claims as {"subject": {"proposals": [], "sessions": [11], "permissions": []}} +} + +test_user_session_not_allow_service_account_wrong_beamline if { + not tiled.user_session with data.diamond.data as diamond_data + with input as {"beamline": "i03", "proposal": 1, "visit": 2} + with data.diamond.policy.token.claims as {"subject": {"proposals": [], "sessions": [], "permissions": ["b07_admin"]}} +} + +test_user_session_allow_service_account_with_beamline if { + tiled.user_session with data.diamond.data as diamond_data + with input as {"beamline": "b07", "proposal": 1, "visit": 2} + with data.diamond.policy.token.claims as { + "subject": {"proposals": [], "sessions": [], "permissions": ["b07_admin"]}, + "fedid": "", + } +} + +test_modify_session_on_proposal if { + tiled.modify_session with data.diamond.data as diamond_data + with input as {"session": "11"} + with data.diamond.policy.token.claims as {"subject": {"proposals": [1], "sessions": [], "permissions": []}} +} + +test_modify_session_on_session if { + tiled.modify_session with data.diamond.data as diamond_data + with input as {"session": "11"} + with data.diamond.policy.token.claims as {"subject": {"proposals": [], "sessions": [11], "permissions": []}} +} + +test_modify_session_on_permission if { + tiled.modify_session with data.diamond.data as diamond_data + with input as {"session": "12"} + with data.diamond.policy.token.claims as { + "subject": { + "proposals": [], + "sessions": [], + "permissions": ["b07_admin"], + }, + "fedid": "", + } +} + +test_user_session_tags_service_account if { + tiled.user_sessions == {11} with data.diamond.data as diamond_data + with data.diamond.policy.token.claims as { + "subject": { + "proposals": [], + "sessions": [11], + "permissions": [], + }, + "fedid": "", + } + tiled.user_sessions == {11, 12} with data.diamond.data as diamond_data + with data.diamond.policy.token.claims as { + "subject": { + "proposals": [1], + "sessions": [], + "permissions": [], + }, + "fedid": "", + } + tiled.user_sessions == {12, 13, 14} with data.diamond.data as diamond_data + with data.diamond.policy.token.claims as { + "subject": { + "proposals": [], + "sessions": [], + "permissions": ["b07_admin"], + }, + "fedid": "", + } +} From 68333b48afcb76bc6118fc59764b3f9707abb823 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:16:42 +0000 Subject: [PATCH 3/5] default fedid for machine users "" --- policy/diamond/policy/tiled/tiled.rego | 24 +++++----- policy/diamond/policy/tiled/tiled_test.rego | 52 ++++++++------------- 2 files changed, 33 insertions(+), 43 deletions(-) diff --git a/policy/diamond/policy/tiled/tiled.rego b/policy/diamond/policy/tiled/tiled.rego index 152a090..3dfc19f 100644 --- a/policy/diamond/policy/tiled/tiled.rego +++ b/policy/diamond/policy/tiled/tiled.rego @@ -23,17 +23,15 @@ scopes := { "tiled-writer" in token.claims.aud } -# Returns the session ID if the subject has write permissions for the -# specific beamline, visit and proposal requested in the input. _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 := to_number(_session) if { session.write_to_beamline_visit _session } -# Service account will need either proposal or session - user_session := to_number(_session) if { input.proposal in token.claims.subject.proposals } @@ -48,12 +46,16 @@ user_session := to_number(_session) if { _session in data.diamond.data.beamlines[input.beamline].sessions } +default fedid := "" + +fedid := token.claims.fedid if token.claims.fedid + # Validates if the subject has permission to modify # the specific session in the input. default modify_session := false modify_session if session.access_session( - token.claims.fedid, + fedid, data.diamond.data.sessions[input.session].proposal_number, data.diamond.data.sessions[input.session].visit_number, ) @@ -73,14 +75,14 @@ modify_session if { ) in beamlines } -subject := data.diamond.data.subjects[token.claims.fedid] if token.claims.fedid +subject := data.diamond.data.subjects[fedid] if fedid else := token.claims.subject if token.claims.subject # Identifies all beamlines the subject is authorized to access # based on their assigned permissions. beamlines contains beamline if { - not admin.is_admin(token.claims.fedid) + not admin.is_admin(fedid) some p in subject.permissions some beamline in object.get(data.diamond.data.admin, p, []) } @@ -93,22 +95,22 @@ beamlines contains beamline if { # 2. Access via beamline-level permissions # 3. Access via proposal-level permissions user_sessions contains "*" if { - admin.is_admin(token.claims.fedid) + admin.is_admin(fedid) } user_sessions contains to_number(session) if { - not admin.is_admin(token.claims.fedid) + not admin.is_admin(fedid) some session in subject.sessions } user_sessions contains to_number(session) if { - not admin.is_admin(token.claims.fedid) + not admin.is_admin(fedid) some beamline in beamlines some session in data.diamond.data.beamlines[beamline].sessions } user_sessions contains to_number(session) if { - not admin.is_admin(token.claims.fedid) + not admin.is_admin(fedid) some p in subject.proposals some i in data.diamond.data.proposals[format_int(p, 10)] some session in i diff --git a/policy/diamond/policy/tiled/tiled_test.rego b/policy/diamond/policy/tiled/tiled_test.rego index bdf7ff1..a16b30c 100644 --- a/policy/diamond/policy/tiled/tiled_test.rego +++ b/policy/diamond/policy/tiled/tiled_test.rego @@ -173,42 +173,30 @@ test_modify_session_on_session if { test_modify_session_on_permission if { tiled.modify_session with data.diamond.data as diamond_data with input as {"session": "12"} - with data.diamond.policy.token.claims as { - "subject": { - "proposals": [], - "sessions": [], - "permissions": ["b07_admin"], - }, - "fedid": "", - } + with data.diamond.policy.token.claims as {"subject": { + "proposals": [], + "sessions": [], + "permissions": ["b07_admin"], + }} } test_user_session_tags_service_account if { tiled.user_sessions == {11} with data.diamond.data as diamond_data - with data.diamond.policy.token.claims as { - "subject": { - "proposals": [], - "sessions": [11], - "permissions": [], - }, - "fedid": "", - } + with data.diamond.policy.token.claims as {"subject": { + "proposals": [], + "sessions": [11], + "permissions": [], + }} tiled.user_sessions == {11, 12} with data.diamond.data as diamond_data - with data.diamond.policy.token.claims as { - "subject": { - "proposals": [1], - "sessions": [], - "permissions": [], - }, - "fedid": "", - } + with data.diamond.policy.token.claims as {"subject": { + "proposals": [1], + "sessions": [], + "permissions": [], + }} tiled.user_sessions == {12, 13, 14} with data.diamond.data as diamond_data - with data.diamond.policy.token.claims as { - "subject": { - "proposals": [], - "sessions": [], - "permissions": ["b07_admin"], - }, - "fedid": "", - } + with data.diamond.policy.token.claims as {"subject": { + "proposals": [], + "sessions": [], + "permissions": ["b07_admin"], + }} } From ea80a92f0ca1eb4e6d8ffb6ee556901d43a493b7 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:22:15 +0000 Subject: [PATCH 4/5] Fix lint issue --- policy/diamond/policy/tiled/tiled.rego | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policy/diamond/policy/tiled/tiled.rego b/policy/diamond/policy/tiled/tiled.rego index 3dfc19f..76b498f 100644 --- a/policy/diamond/policy/tiled/tiled.rego +++ b/policy/diamond/policy/tiled/tiled.rego @@ -48,7 +48,7 @@ user_session := to_number(_session) if { default fedid := "" -fedid := token.claims.fedid if token.claims.fedid +fedid := token.claims.fedid # Validates if the subject has permission to modify # the specific session in the input. From 6f9d74dc2d45728c1f8979ce4cddc3de9186678e Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Thu, 19 Feb 2026 09:56:17 +0000 Subject: [PATCH 5/5] Use token.claims.fedid --- policy/diamond/policy/tiled/tiled.rego | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policy/diamond/policy/tiled/tiled.rego b/policy/diamond/policy/tiled/tiled.rego index 76b498f..783e7e7 100644 --- a/policy/diamond/policy/tiled/tiled.rego +++ b/policy/diamond/policy/tiled/tiled.rego @@ -75,7 +75,7 @@ modify_session if { ) in beamlines } -subject := data.diamond.data.subjects[fedid] if fedid +subject := data.diamond.data.subjects[token.claims.fedid] if token.claims.fedid else := token.claims.subject if token.claims.subject