-
Notifications
You must be signed in to change notification settings - Fork 5
Add policy for Tiled scopes and filters #293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
1ac9dee to
8e884df
Compare
8e884df to
ce394ee
Compare
|
|
||
| scopes_for(claims) := read_scopes | write_scopes if { | ||
| "azp" in object.keys(claims) | ||
| endswith(claims.azp, "-blueapi") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is azp?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"authorizing party"- the client-id that the token was minted for- i.e. this token has been one that a blueapi instance has set as headers to a request to tiled, not one that has been set in a tiled browser client or tiled login flow.
This of course then only works if your request originated with blueapi, meaning as soon as there's an experiment UI in front of that request it stops working. When we make use of token-exchange flow in keycloak from the incoming request token, I think we can request from keycloak a token with "aud": ["blueapi", "tiled"] and check that both audiences are present, then configure our services to never give both audiences otherwise? Or make use of some optional scope for the blueapi client - I can't see many other ways of checking that the request has come from a blueapi instance (which should be the only thing permitted to write to tiled) without checking something about the party sending the token.
| scopes_for(claims) := read_scopes if { | ||
| "azp" in object.keys(claims) | ||
| not endswith(claims.azp, "-blueapi") | ||
| } | ||
|
|
||
| scopes_for(claims) := read_scopes if { | ||
| not "azp" in object.keys(claims) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these can be combined?
| scopes_for(claims) := read_scopes if { | |
| "azp" in object.keys(claims) | |
| not endswith(claims.azp, "-blueapi") | |
| } | |
| scopes_for(claims) := read_scopes if { | |
| not "azp" in object.keys(claims) | |
| } | |
| scopes_for(claims) := read_scopes if { | |
| not "azp" in object.keys(claims) | |
| not endswith(claims.azp, "-blueapi") | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tiled_test.rego:12 | | Fail data.diamond.policy.tiled.scopes = data.diamond.policy.tiled.read_scopes with data.diamond.policy.token.claims as {}
Looks like it fails the tests when "azp" not in token.claims? Presumably the first line is true, then it checks the second line and it isn't defined, so it returns no scopes?
There is already a token if we have made it here, it just doesn't have the azp claim- I believe it is optional, so an authenticated user could have make a request without it (I think right now we're only getting it because our audience is always account rather than <client-id>.
tpoliaw
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think tiled specific policy should be deployed alongside tiled (see application policy dev guide). The user_sessions policy could be in the core but would be better as a list of sessions instead of a list of JSON formatted strings.
Could we have some form of admin handling in tiled? Presumably for an admin, this list is every session that's ever happened at diamond?
The rust update should be its own PR.
| some session in data.diamond.data.sessions | ||
| access_session(token.claims.fedid, session.proposal_number, session.visit_number) | ||
| user_session := sprintf( | ||
| `{"proposal": %d, "visit": %d, "beamline": "%s"}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we formatting JSON here? Doesn't OPA already return its data in JSON format?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OPA returns it in JSON but the inputs of the policy is proposal (and so we want tiled to store that key) but the key in the session object has proposal_number - same for visit[_number].
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could store proposal_number, visit_number and a json blob and have tiled do the mutation, but I think it should naively make an input from the access_blob https://github.com/bluesky/tiled/blob/397e6e6c149b9b718b5308a29c8a92692ba56d7e/tiled/access_control/dls.py#L64
We could keep it fairly self contained:
class DiamondAccessBlob(TypedDict):
proposal: int = Field(serialization_alias="proposal_number")
visit: int = Field(serialization_alias="visit_number")
beamline: str
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At some point we'd like to replace the access_blob with the session_id (either by Tiled swapping the blob it's given with proposal, visit, beamline or by blueapi querying OPA to get the session_id of the same) as it allows a faster comparison with an int
That makes orchestrating changes in Tiled specific policies much simpler. We don't have an application in the DLS namespace to own the policies, could use Glazed or make its own one.
Yes, which is why it takes ~5s currently for any calls we make. Tiled shouldn't have any knowledge about who is an admin, but we can return an empty list for super_admin- Tiled does not filter any elements out if given an empty list? |
This is wrong, Tiled filters out all elements if given an empty list, but does not filter if returned None instead of a list. I think this is the opposite way than we would expect? |
|
I think that way makes sense? Only filter if you're given a list of filters. If you pass |
It could be its own repo but deployed alongside our tiled deployment(s). |
|
Policy lint issues fixed in this #297 |
|
superseded by 297 |
I cannot modify the target for this PR to be into #291 as it is in Zoheb's fork, but the changes from that are required to get the CI passing again so they are included. #291 should be reviewed first.
Questions around: where should the Tiled specific policy live: a. the get-all-sessions for a user query, which is used for tiled but may find uses elsewhere, and given the time the query takes may benefit from a communal effort to improve and b. the Tiled specific scope query, which is Tiled specific (it may unilaterally change implementation but not return values) but is an authorization decision?