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 10, 2024
1 parent 89ad232 commit afc4380
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 2 deletions.
15 changes: 15 additions & 0 deletions policy/diamond/policy/admin/admin.rego
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,18 @@ 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
}

admin if is_admin(input.user) # regal ignore:rule-name-repeats-package

else := false if {
# hack to ensure rule is defined when user is in input
u := input.user
}

beamline_admin if is_beamline_admin(input.user, input.beamline)

else := false if {
# hack to ensure rule is defined when both user and beamline are in input
user := input.user
beamline := input.beamline
}
61 changes: 61 additions & 0 deletions policy/diamond/policy/admin/admin_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,64 @@ test_non_beamline_admin if {
test_super_admin_not_beamline_admin if {
not admin.is_beamline_admin("carol", "b07") 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
16 changes: 16 additions & 0 deletions policy/diamond/policy/proposal/proposal.rego
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,19 @@ access_proposal(subject, proposal_number) if admin.is_admin(subject)

# Allow if subject is on proposal
access_proposal(subject, proposal_number) if on_proposal(subject, proposal_number)

access if access_proposal(input.user, input.proposal)

else := false if {
# Ensure rule has value if user and proposal are in input
user := input.user
proposal := input.proposal
}

named_user if on_proposal(input.user, input.proposal)

else := false if {
# Ensure rule has value if user and proposal are in input
user := input.user
proposal := 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
27 changes: 25 additions & 2 deletions policy/diamond/policy/session/session.rego
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ 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
Expand All @@ -23,11 +23,34 @@ access_session(subject, proposal_number, visit_number) if admin.is_admin(subject

# 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))
admin.is_beamline_admin(subject, beamline_for(proposal_number, visit_number))
}

# 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)

# default access := false
access if access_session(input.user, input.proposal, input.visit)

else := false if {
# This is a hack to only set the false value if all parameters are defined
# There must be a better way of doing this
user := input.user
prop := input.proposal
visit := input.visit
}

named_user if on_session(input.user, input.proposal, input.visit)

else := false if {
user := input.user
prop := input.proposal
visit := 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 afc4380

Please sign in to comment.