Skip to content

Commit

Permalink
Add policy rules for session and proposal access
Browse files Browse the repository at this point in the history
* session.access: user can access session
* session.named_user: user is a named member of the visit
* session.matches_beamline: visit is on the given beamline
* session.session_beamline: beamline for the given visit

* proposal.access: user can access proposal
* proposal.named_user: user is a named user on the proposal

* admin.admin: user is super admin
* admin.beamline_admin: user is admin for the given beamline

Rules are only defined if the required fields are included in the input.
`admin.beamline_admin` refers to the beamline passed as `input.beamline`
not as the beamline for the session defined by `proposal`+`visit`.

Previous function `session.beamline` has been renamed to `beamline_for`
to distinguish it from the `session.beamline` rule.
  • Loading branch information
tpoliaw committed Oct 11, 2024
1 parent 89ad232 commit a241bbb
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 24 deletions.
15 changes: 9 additions & 6 deletions policy/diamond/policy/admin/admin.rego
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package diamond.policy.admin

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
is_beamline_admin[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[input.user] # regal ignore:rule-name-repeats-package

beamline_admin := input.beamline in object.get(is_beamline_admin, input.user, [])
89 changes: 75 additions & 14 deletions policy/diamond/policy/admin/admin_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,91 @@ 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_is_beamline_admin_subject_beamline if {
admin.is_beamline_admin.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_is_beamline_admin_for_group_admin if {
admin.is_beamline_admin.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_is_beamline_admin_for_non_beamline_admin if {
not "alice" in admin.is_beamline_admin 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_is_beamline_admin_for_admin if {
not "carol" in admin.is_beamline_admin with data.diamond.data as diamond_data
}

test_admin_rule_for_admin if {
admin.admin with input as {"user": "carol"}
with data.diamond.data as diamond_data
}

test_admin_rule_for_non_admin if {
not admin.admin with input as {"user": "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 input 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 {"user": "bob", "beamline": "b07"}
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 {"user": "carol", "beamline": "b07"}
with data.diamond.data as diamond_data
}

test_beamline_admin_rule_for_non_beamline_admin if {
not admin.beamline_admin with input as {"user": "alice", "beamline": "b07"}
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 {"user": "bob", "beamline": "i07"}
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 input as {"user": "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
10 changes: 9 additions & 1 deletion policy/diamond/policy/proposal/proposal.rego
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ package diamond.policy.proposal
import data.diamond.policy.admin
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(input.user, input.proposal)

named_user := on_proposal(input.user, input.proposal)
53 changes: 53 additions & 0 deletions policy/diamond/policy/proposal/proposal_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,56 @@ 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 {"user": "alice", "proposal": 1}
with data.diamond.data as diamond_data
}

test_named_user_rule_for_unnamed_user if {
not proposal.named_user with input as {"user": "carol", "proposal": 1}
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 input as {"user": "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 {"user": "carol", "proposal": 1}
with data.diamond.data as diamond_data
}

test_access_rule_for_named_user if {
proposal.access with input as {"user": "alice", "proposal": 1}
with data.diamond.data as diamond_data
}

test_access_rule_for_unnamed_user if {
not proposal.access with input as {"user": "oscar", "proposal": 1}
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 input as {"user": "alice"}
with data.diamond.data as diamond_data
}

else := true # regal ignore:default-over-else
20 changes: 17 additions & 3 deletions policy/diamond/policy/session/session.rego
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,44 @@ import data.diamond.policy.admin
import data.diamond.policy.proposal
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
subject_session.proposal_number == proposal_number
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))
beamline_for(proposal_number, visit_number) in admin.is_beamline_admin[subject] # regal ignore:external-reference
}

# Allow if subject on proposal which contains session
access_session(subject, proposal_number, visit_number) if proposal.on_proposal(subject, proposal_number)

# 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(input.user, input.proposal, input.visit)

named_user := on_session(input.user, 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)
101 changes: 101 additions & 0 deletions policy/diamond/policy/session/session_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,104 @@ 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 {"user": "alice", "proposal": 1, "visit": 1}
with data.diamond.data as diamond_data
}

test_access_rule_for_beamline_admin if {
session.access with input as {"user": "bob", "proposal": 1, "visit": 2}
with data.diamond.data as diamond_data
}

test_access_rule_for_super_admin if {
session.access with input as {"user": "carol", "proposal": 1, "visit": 2}
with data.diamond.data as diamond_data
}

test_access_rule_for_non_user if {
not session.access with input as {"user": "oscar", "proposal": 1, "visit": 1}
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 {"user": "bob", "visit": 2}
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 {"user": "bob", "proposal": 2}
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 {"user": "bob", "proposal": 1, "visit": 1}
with data.diamond.data as diamond_data
}

test_named_user_rule_for_unnamed_user if {
not session.named_user with input as {"user": "oscar", "proposal": 1, "visit": 1}
with data.diamond.data as diamond_data
}

test_named_user_rule_for_super_admin if {
not session.named_user with input as {"user": "alice", "proposal": 1, "visit": 1}
with data.diamond.data as diamond_data
}

test_named_user_rule_for_beamline_admin if {
not session.named_user with input as {"user": "bob", "proposal": 1, "visit": 2}
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 {"user": "alice", "proposal": 1, "visit": 2}
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"
}

0 comments on commit a241bbb

Please sign in to comment.