diff --git a/policy/diamond/policy/admin/admin.rego b/policy/diamond/policy/admin/admin.rego index f4946d38..ee94ba4c 100644 --- a/policy/diamond/policy/admin/admin.rego +++ b/policy/diamond/policy/admin/admin.rego @@ -1,12 +1,16 @@ package diamond.policy.admin +import data.diamond.policy.token import rego.v1 -is_admin(subject) if { - "super_admin" in data.diamond.data.subjects[subject].permissions # regal ignore:external-reference -} +is_admin[subject] := "super_admin" in data.diamond.data.subjects[subject].permissions -is_beamline_admin(subject, beamline) if { - some admin in data.diamond.data.subjects[subject].permissions - beamline in data.diamond.data.admin[admin] # regal ignore:external-reference +beamline_admin_for_subject[subject] contains beamline if { + some subject + some role in data.diamond.data.subjects[subject].permissions + some beamline in data.diamond.data.admin[role] } + +admin := is_admin[token.claims.fedid] # regal ignore:rule-name-repeats-package + +beamline_admin := input.beamline in object.get(beamline_admin_for_subject, token.claims.fedid, []) diff --git a/policy/diamond/policy/admin/admin_test.rego b/policy/diamond/policy/admin/admin_test.rego index e9d36f7e..6f9d2257 100644 --- a/policy/diamond/policy/admin/admin_test.rego +++ b/policy/diamond/policy/admin/admin_test.rego @@ -32,30 +32,95 @@ diamond_data := { "admin": {"b07_admin": ["b07"], "group_admin": ["b07", "i07"]}, } -test_super_admin_subject if { - admin.is_admin("carol") with data.diamond.data as diamond_data +test_is_admin_for_admin if { + admin.is_admin.carol with data.diamond.data as diamond_data } -test_beamline_admin_subject_beamline if { - admin.is_beamline_admin("bob", "b07") with data.diamond.data as diamond_data +test_beamline_admin_for_subject_for_beamline_admin if { + admin.beamline_admin_for_subject.bob == {"b07"} with data.diamond.data as diamond_data } -test_group_admin_subject_beamline if { - admin.is_beamline_admin("oscar", "b07") with data.diamond.data as diamond_data +test_beamlines_admin_for_subject_for_group_admin if { + admin.beamline_admin_for_subject.oscar == {"b07", "i07"} with data.diamond.data as diamond_data } -test_non_admin if { - not admin.is_admin("alice") with data.diamond.data as diamond_data +test_is_admin_for_non_admin if { + not admin.is_admin.alice with data.diamond.data as diamond_data } -test_beamline_admin_not_admin if { - not admin.is_admin("bob") with data.diamond.data as diamond_data +test_is_admin_for_beamline_admin_not_admin if { + not admin.is_admin.bob with data.diamond.data as diamond_data } -test_non_beamline_admin if { - not admin.is_beamline_admin("alice", "b07") with data.diamond.data as diamond_data +test_beamline_admin_for_subject_for_non_beamline_admin if { + not "alice" in admin.beamline_admin_for_subject with data.diamond.data as diamond_data } -test_super_admin_not_beamline_admin if { - not admin.is_beamline_admin("carol", "b07") with data.diamond.data as diamond_data +test_beamline_admin_for_subject_for_admin if { + not "carol" in admin.beamline_admin_for_subject with data.diamond.data as diamond_data } + +test_admin_rule_for_admin if { + admin.admin with data.diamond.policy.token.claims as {"fedid": "carol"} + with data.diamond.data as diamond_data +} + +test_admin_rule_for_non_admin if { + not admin.admin with data.diamond.policy.token.claims as {"fedid": "bob"} + with data.diamond.data as diamond_data +} + +# If no user is passed as input, the rule should be undefined +test_admin_rule_for_no_user := false if { + local_admin := admin.admin with data.diamond.policy.token.claims as {} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_beamline_admin_rule_for_beamline_admin if { + admin.beamline_admin with input as {"beamline": "b07"} + with data.diamond.policy.token.claims as {"fedid": "bob"} + with data.diamond.data as diamond_data +} + +# super_admin can access anything but they still aren't automatically beamline admins +test_beamline_admin_rule_for_super_admin if { + not admin.beamline_admin with input as {"beamline": "b07"} + with data.diamond.policy.token.claims as {"fedid": "carol"} + with data.diamond.data as diamond_data +} + +test_beamline_admin_rule_for_non_beamline_admin if { + not admin.beamline_admin with input as {"beamline": "b07"} + with data.diamond.policy.token.claims as {"fedid": "alice"} + with data.diamond.data as diamond_data +} + +test_beamline_admin_rule_for_wrong_beamline_admin if { + # bob is only beamline admin for b07 + not admin.beamline_admin with input as {"beamline": "i07"} + with data.diamond.policy.token.claims as {"fedid": "bob"} + with data.diamond.data as diamond_data +} + +test_beamline_admin_rule_for_no_user := false if { + local_admin := admin.beamline_admin with input as {"beamline": "i07"} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_beamline_admin_rule_for_no_beamline := false if { + local_admin := admin.beamline_admin with data.diamond.policy.token.claims as {"fedid": "bob"} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_beamline_admin_rule_for_no_input := false if { + local_admin := admin.beamline_admin with input as {} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else diff --git a/policy/diamond/policy/proposal/proposal.rego b/policy/diamond/policy/proposal/proposal.rego index 727f5614..f3c28052 100644 --- a/policy/diamond/policy/proposal/proposal.rego +++ b/policy/diamond/policy/proposal/proposal.rego @@ -1,14 +1,23 @@ package diamond.policy.proposal import data.diamond.policy.admin +import data.diamond.policy.token import rego.v1 +default on_proposal(_, _) := false + on_proposal(subject, proposal_number) if { proposal_number in data.diamond.data.subjects[subject].proposals # regal ignore:external-reference } +default access_proposal(_, _) := false + # Allow if subject has super_admin permission -access_proposal(subject, proposal_number) if admin.is_admin(subject) +access_proposal(subject, proposal_number) if admin.is_admin[subject] # regal ignore:external-reference # Allow if subject is on proposal access_proposal(subject, proposal_number) if on_proposal(subject, proposal_number) + +access := access_proposal(token.claims.fedid, input.proposal) + +named_user := on_proposal(token.claims.fedid, input.proposal) diff --git a/policy/diamond/policy/proposal/proposal_test.rego b/policy/diamond/policy/proposal/proposal_test.rego index d2d2a82e..5eeba4ce 100644 --- a/policy/diamond/policy/proposal/proposal_test.rego +++ b/policy/diamond/policy/proposal/proposal_test.rego @@ -43,3 +43,61 @@ test_member_on_proposal if { test_admin_not_on_proposal if { not proposal.on_proposal("carol", 1) with data.diamond.data as diamond_data } + +test_named_user_rule_for_named_user if { + proposal.named_user with input as {"proposal": 1} + with data.diamond.policy.token.claims as {"fedid": "alice"} + with data.diamond.data as diamond_data +} + +test_named_user_rule_for_unnamed_user if { + not proposal.named_user with input as {"proposal": 1} + with data.diamond.policy.token.claims as {"fedid": "carol"} + with data.diamond.data as diamond_data +} + +test_named_user_rule_for_no_user := false if { + named := proposal.named_user with input as {"proposal": 1} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_named_user_rule_for_no_proposal := false if { + named := proposal.named_user with data.diamond.policy.token.claims as {"fedid": "carol"} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_access_rule_for_super_admin if { + proposal.access with input as {"proposal": 1} + with data.diamond.policy.token.claims as {"fedid": "carol"} + with data.diamond.data as diamond_data +} + +test_access_rule_for_named_user if { + proposal.access with input as {"proposal": 1} + with data.diamond.policy.token.claims as {"fedid": "alice"} + with data.diamond.data as diamond_data +} + +test_access_rule_for_unnamed_user if { + not proposal.access with input as {"proposal": 1} + with data.diamond.policy.token.claims as {"fedid": "oscar"} + with data.diamond.data as diamond_data +} + +test_access_rule_for_no_user := false if { + access := proposal.access with input as {"proposal": 1} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_access_rule_for_no_proposal := false if { + access := proposal.access with data.diamond.policy.token.claims as {"fedid": "alice"} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else diff --git a/policy/diamond/policy/session/session.rego b/policy/diamond/policy/session/session.rego index b8a9b697..6cf6cf27 100644 --- a/policy/diamond/policy/session/session.rego +++ b/policy/diamond/policy/session/session.rego @@ -2,15 +2,18 @@ package diamond.policy.session import data.diamond.policy.admin import data.diamond.policy.proposal +import data.diamond.policy.token import rego.v1 -beamline(proposal_number, visit_number) := beamline if { +beamline_for(proposal_number, visit_number) := beamline if { proposal := data.diamond.data.proposals[format_int(proposal_number, 10)] # regal ignore:external-reference session_id := proposal.sessions[format_int(visit_number, 10)] session := data.diamond.data.sessions[format_int(session_id, 10)] # regal ignore:external-reference beamline := session.beamline } +default on_session(_, _, _) := false + on_session(subject, proposal_number, visit_number) if { some session_id in data.diamond.data.subjects[subject].sessions # regal ignore:external-reference subject_session := data.diamond.data.sessions[format_int(session_id, 10)] # regal ignore:external-reference @@ -18,12 +21,15 @@ on_session(subject, proposal_number, visit_number) if { subject_session.visit_number == visit_number } +default access_session(_, _, _) := false + # Allow if subject has super_admin permission -access_session(subject, proposal_number, visit_number) if admin.is_admin(subject) +access_session(subject, proposal_number, visit_number) if admin.is_admin[subject] # regal ignore:external-reference # Allow if subject is admin for beamline containing session access_session(subject, proposal_number, visit_number) if { - admin.is_beamline_admin(subject, beamline(proposal_number, visit_number)) + # regal ignore:external-reference + beamline_for(proposal_number, visit_number) in admin.beamline_admin_for_subject[subject] } # Allow if subject on proposal which contains session @@ -31,3 +37,13 @@ access_session(subject, proposal_number, visit_number) if proposal.on_proposal(s # Allow if subject directly on session access_session(subject, proposal_number, visit_number) if on_session(subject, proposal_number, visit_number) + +# Rules depending on input data + +access := access_session(token.claims.fedid, input.proposal, input.visit) + +named_user := on_session(token.claims.fedid, input.proposal, input.visit) + +matches_beamline := input.beamline == beamline_for(input.proposal, input.visit) # regal ignore:boolean-assignment + +beamline := beamline_for(input.proposal, input.visit) diff --git a/policy/diamond/policy/session/session_test.rego b/policy/diamond/policy/session/session_test.rego index 583b634b..a81d1e92 100644 --- a/policy/diamond/policy/session/session_test.rego +++ b/policy/diamond/policy/session/session_test.rego @@ -69,3 +69,115 @@ test_non_member_denied if { test_admin_not_on_session if { not session.on_session("carol", 1, 1) with data.diamond.data as diamond_data } + +test_access_rule_for_named_user if { + session.access with input as {"proposal": 1, "visit": 1} + with data.diamond.policy.token.claims as {"fedid": "alice"} + with data.diamond.data as diamond_data +} + +test_access_rule_for_beamline_admin if { + session.access with input as {"proposal": 1, "visit": 2} + with data.diamond.policy.token.claims as {"fedid": "bob"} + with data.diamond.data as diamond_data +} + +test_access_rule_for_super_admin if { + session.access with input as {"proposal": 1, "visit": 2} + with data.diamond.policy.token.claims as {"fedid": "carol"} + with data.diamond.data as diamond_data +} + +test_access_rule_for_non_user if { + not session.access with input as {"proposal": 1, "visit": 1} + with data.diamond.policy.token.claims as {"fedid": "oscar"} + with data.diamond.data as diamond_data +} + +test_access_rule_for_no_user := false if { + access := session.access with input as {"proposal": 1, "visit": 2} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_access_rule_for_no_proposal := false if { + access := session.access with input as {"visit": 2} + with data.diamond.policy.token.claims as {"fedid": "bob"} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_access_rule_for_no_visit := false if { + access := session.access with input as {"proposal": 2} + with data.diamond.policy.token.claims as {"fedid": "bob"} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_named_user_rule_for_named_user if { + session.named_user with input as {"proposal": 1, "visit": 1} + with data.diamond.policy.token.claims as {"fedid": "bob"} + with data.diamond.data as diamond_data +} + +test_named_user_rule_for_unnamed_user if { + not session.named_user with input as {"proposal": 1, "visit": 1} + with data.diamond.policy.token.claims as {"fedid": "oscar"} + with data.diamond.data as diamond_data +} + +test_named_user_rule_for_super_admin if { + not session.named_user with input as {"proposal": 1, "visit": 1} + with data.diamond.policy.token.claims as {"fedid": "alice"} + with data.diamond.data as diamond_data +} + +test_named_user_rule_for_beamline_admin if { + not session.named_user with input as {"proposal": 1, "visit": 2} + with data.diamond.policy.token.claims as {"fedid": "bob"} + with data.diamond.data as diamond_data +} + +test_named_user_rule_for_named_proposal if { + # Users on the proposal can access the session but aren't named on it + not session.named_user with input as {"proposal": 1, "visit": 2} + with data.diamond.policy.token.claims as {"fedid": "alice"} + with data.diamond.data as diamond_data +} + +test_matches_beamline_rule_for_match if { + session.matches_beamline with input as {"beamline": "b07", "proposal": 1, "visit": 2} + with data.diamond.data as diamond_data +} + +test_matches_beamline_rule_for_non_match if { + not session.matches_beamline with input as {"beamline": "b07", "proposal": 1, "visit": 1} + with data.diamond.data as diamond_data +} + +test_matches_beamline_rule_for_no_beamline := false if { + match := session.matches_beamline with input as {"proposal": 1, "visit": 1} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_matches_beamline_rule_for_no_visit := false if { + match := session.matches_beamline with input as {"beamline": "b07"} + with data.diamond.data as diamond_data +} + +else := true # regal ignore:default-over-else + +test_session_beamline if { + bl1 := session.beamline with input as {"proposal": 1, "visit": 1} + with data.diamond.data as diamond_data + bl2 := session.beamline with input as {"proposal": 1, "visit": 2} + with data.diamond.data as diamond_data + + bl1 == "i03" + bl2 == "b07" +}