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.

User is determined from token passed as `input.token`.
  • Loading branch information
tpoliaw committed Oct 31, 2024
1 parent f5bd52a commit 49bddb5
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 24 deletions.
16 changes: 10 additions & 6 deletions policy/diamond/policy/admin/admin.rego
Original file line number Diff line number Diff line change
@@ -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, [])
93 changes: 79 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,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
11 changes: 10 additions & 1 deletion policy/diamond/policy/proposal/proposal.rego
Original file line number Diff line number Diff line change
@@ -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)
58 changes: 58 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,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
22 changes: 19 additions & 3 deletions policy/diamond/policy/session/session.rego
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,48 @@ 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
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))
# 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
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(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)
112 changes: 112 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,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"
}

0 comments on commit 49bddb5

Please sign in to comment.