Closed jakedt closed 1 year ago
Adding an example of such policy:
Type:1
and subject Type:2
tag
subject iff object was created after subjectThis would be extremely useful for my use-cases as my current authorization system has identities with multiple attributes (e.g. app name, a few stable attributes like region, etc.)
I am curious about a few things:
many of my use cases involve a situation where I need to give contextually specific permission, where a specific user X can send specific object Y only to specific destination Z
One of the other zanzibar-like implementations seems to be addressing this by taking in "contextual tuples" in the "check permission" endpoint, which means the check behaves as if those relationships existed in the graph. I don't like that approach because it puts back authz logic in the check invoker, i think yours is better, however, I wonder if the comparison language proposed here will be expressive enough to handle all possible needs. I think this downside would be mitigated if the available comparison functions are pluggable.
Here is a bit more detail on the use-cases I'm looking to support:
The requestor would supply an app identity with
Application {
Name: app,
Stack: foo,
Detail: bar,
Instance-id: i-12358,
Region: us-east-1,
Executor: sys2,
ExtendedAttributes: {“k1”: “123”, “k2”: “bar”, “k3”: “6790”}
}
We’d write a relation to spice similar to the following:
idstore/group:authsvc#member@spin/caveated_app:app[detail="bar" stack="foo"]
When a requestor makes a permission check, in this instance checking for #member
in authsvc
, spice would build a path to the app and then check for a relationship with matching caveats. Unspecified caveats would be treated as a match. A relation with a complete match would result in an allow.
We do have certain cases where more complex attributes can be present, specifically within an application’s extended attributes. This is an untyped json blob; though currently all properties are string-ly typed.
In addition to simple attribute matching demonstrated in use-case 1, we’d also want to match on subsets of these complex attributes
idstore/group:authsvc#member@spin/caveated_app:app[detail="bar" stack="foo" extended_attributes=contains({“k1”: “123”, “k2”: “bar”})]
Again similar to use-case 1 the entire app identity would be supplied as part of the permission check and caveats would be required to match when specified.
Hey folks! 👋🏻 We are starting to work on caveats and we would love to hear more real-life use-cases that will help us validate the implementation meets your needs. Feel free to share them here!
We have the following use case! A health insurance member
might be a dependent
on someone else's plan -- that person being the subscriber
. We want a subscriber
to be able to see all of their dependent
's claims
if the dependent is under 12 years old, and SOME of their dependent's claims
(the ones not marked "sensitive") if they're between 12 and 18.
I also have a use case that has been popping up a lot.
If a resource is no longer mutable after some condition has occurred, that resource should not be editable by anyone. Some examples that I've encountered so far:
legal_filing
has a creator
that is permitted to edit
the filing, but once it has been submitted (submitted == true
) the filing should not be editable even by the creator
. Many other user
s should still be able to view it.At Tidepool, for compliance reasons, we have the need to support time delineated data sharing. We store tie series data from personal health devices (e.g. continuous glucose meters) and we allow our users to share their data with clinics to receive treatment. However, clinics must be able access data that they had access to even if the patient revokes the sharing relationship, because the data was used for providing treatment. So each sharing relationship needs to have a start_date
attribute and optional end_date
. When a patient revokes the sharing relationship we would need to set the end_date
attribute and enforce access to data based on those attributes.
I believe our use case will be supported by the initial implementation but I thought it would be useful to share it anyway.
Remaining work:
I'm trying to understand how caveats can help with the following use case. { caveat need_mfa (mfa:bool) { mfa == true }
caveat dont_need_mfa(mfa: bool) { true }
definition user{}
definition role { relation viewer: user }
definition resource { relation viewer: role with dont_need_mfa }
definition secure_resource { relation viewer: role with need_mfa } }
secure_resource:res1[need_mfa]#viewer@user:user1 resource:res2[dont_need_mfa]#viewer@user:user1
secure_resource:res1#viewer@user:user1 with {mfa:false} -> gives false resource:res2#viewer@user:user1 with {mfa:false} gives true
In other words the runtime context is on the user->role relation but the caveat is on the role->resource relation. The context is propagated down to the caveat.
The search through a path to join the object and subject seems fundamental to AuthZed and it seems that a caveat could be applied at any step in the process to see if the subject is part of the userset. But in order to make that judgement we need the subjects context.
Or am I missing something?
The search through a path to join the object and subject seems fundamental to AuthZed and it seems that a caveat could be applied at any step in the process to see if the subject is part of the userset. But in order to make that judgement we need the subjects context.
Can you clarify what you mean by "you need the subjects context" here?
The search through a path to join the object and subject seems fundamental to AuthZed and it seems that a caveat could be applied at any step in the process to see if the subject is part of the userset. But in order to make that judgement we need the subjects context.
Can you clarify what you mean by "you need the subjects context" here?
the subject has an mfa status, that is their context. They are related to a role, without caveat. The role is related to a resource, with a caveat. The parameter to the resource caveat is the context of the subject.
Not to jump to solutions but it is as if the context from the original permission check passes through the resolution chain and is applied to every caveat that is encountered along the way based on some matching criteria. The chain could be arbitrarily long, and the permission check could have an arbitrary set of context N/V. When a caveat is encountered if a context name matches a caveat parameter name it is applied. If the caveat is satisfied the subject joins the set.
the subject has an mfa status, that is their context. They are related to a role, without caveat. The role is related to a resource, with a caveat. The parameter to the resource caveat is the context of the subject.
Yep, each has its own context, with the overall context being given in the CheckPermission
call.
Not to jump to solutions but it is as if the context from the original permission check passes through the resolution chain and is applied to every caveat that is encountered along the way based on some matching criteria. The chain could be arbitrarily long, and the permission check could have an arbitrary set of context N/V. When a caveat is encountered if a context name matches a caveat parameter name it is applied. If the caveat is satisfied the subject joins the set.
Sort of; what actually happens is that we build a caveat expression as we resolve the full path and, at the end, if the result is caveated (e.g. it has an expression), the whole expression is evaluated to determine whether the permission is has_permission
or not.
the subject has an mfa status, that is their context. They are related to a role, without caveat. The role is related to a resource, with a caveat. The parameter to the resource caveat is the context of the subject.
Yep, each has its own context, with the overall context being given in the
CheckPermission
call.Not to jump to solutions but it is as if the context from the original permission check passes through the resolution chain and is applied to every caveat that is encountered along the way based on some matching criteria. The chain could be arbitrarily long, and the permission check could have an arbitrary set of context N/V. When a caveat is encountered if a context name matches a caveat parameter name it is applied. If the caveat is satisfied the subject joins the set.
Sort of; what actually happens is that we build a caveat expression as we resolve the full path and, at the end, if the result is caveated (e.g. it has an expression), the whole expression is evaluated to determine whether the permission is
has_permission
or not.
I can confirm, with pleasant surprise, this it "just works". Here's my schema, relationships and assertions. (its my own format, based on the playground download format with my extensions for caveats. I have a python program that parses this and interacts with the spicedb API. )
You can see that I have caveats at the resource level but apply the inputs at the user level, even though the user doesn't have a caveat. The context is being passed down and applied correctly to control access to the resource. Awesome!!
As an afterthought I made the secure resource to accept either kind of role and now I can declare at the time of defining the relationship. So res1 and res3 have different mfa requirements even though they are in the same type. Awesomer!!!
schema: |- caveat need_mfa(mfa string) { mfa == 'true' } caveat dont_need_mfa(mfa string) { mfa == mfa }
definition user{}
definition role { relation viewer: user }
definition resource { relation roles: role with dont_need_mfa permission viewer = roles->viewer }
definition secure_resource { relation roles: role with need_mfa | role with dont_need_mfa permission viewer = roles->viewer } relationships: |- secure_resource:res1#roles@role:role1[need_mfa] secure_resource:res3#roles@role:role1[dont_need_mfa] resource:res2#roles@role:role1[dont_need_mfa] role:role1#viewer@user:user1 assertions: assertTrue:
As an afterthought I made the secure resource to accept either kind of role and now I can declare at the time of defining the relationship. So res1 and res3 have different mfa requirements even though they are in the same type. Awesomer!!!
You can elide this by declaring that a role
is allowed directly, without caveat:
relation roles: role with need_mfa | role
Then you can just write a relationship without the caveat at all:
secure_resource:res1#roles@role:role1[need_mfa]
secure_resource:res3#roles@role:role1
Will be a bit faster that way, since in the case where an mfa
is not needed, the system can just elide caveat execution completely
As an afterthought I made the secure resource to accept either kind of role and now I can declare at the time of defining the relationship. So res1 and res3 have different mfa requirements even though they are in the same type. Awesomer!!!
You can elide this by declaring that a
role
is allowed directly, without caveat:relation roles: role with need_mfa | role
Then you can just write a relationship without the caveat at all:
secure_resource:res1#roles@role:role1[need_mfa] secure_resource:res3#roles@role:role1
Will be a bit faster that way, since in the case where an
mfa
is not needed, the system can just elide caveat execution completely
There are two cases: 1) caveat or none, or 2) this caveat or that caveat. Both can be expressed
There are two cases: 1) caveat or none, or 2) this caveat or that caveat. Both can be expressed
Yep! For reference:
relation roles: role with need_mfa | role with another_caveat | role
Will allow either caveat or no caveat
Remaining items to remove experimental flag:
Goal
Let people handle their abac-y style needs in a Zanzibar first way.
Use Cases
Proposal
Allow small fragments of policy to be associated with individual relationships in a new field called “caveats”. As we attempt to evaluate permissions these pieces of policy will be combined and surfaced as immutable caveats that apply to the result sets as they are collected. Before the result is returned to the user, a final policy is assembled and evaluated against user-supplied attributes, and a final decision is made.
Because the caveats are immutable and apply to the sub-problem, they can be cached at every level of the decision making process.
Examples
Union Caveat
Schema and Relationships
Resolution
Exclusion Caveat
Schema and Relationships
Resolution
Intersection Caveat
Schema and Relationships
Resolution
Open Questions
Previous Work
200
158