Closed zepatrik closed 5 months ago
That article is an awesome explanation of what is needed and the current limitation of Keto.
As discussed on Slack, I am still not satiesfied about the following point:
This makes sense because if a certain action should no longer be allowed for a certain role, there should be no need to iterate over every object to remove a permission.
By looking at https://gruchalski.com/posts/2021-05-15-rbac-with-ory-keto/#fry-has-fried-the-production the proposed solution does indeed require manual removal of the bindings from all objects.
This means that if I have 100.000 objects where I assigned the role fast-dev-director
and I want to revoke the create
permission I need to iterate over all of them with a DELETE
call:
curl --silent -X DELETE 'http://localhost:4467/relation-tuples?namespace=default-namespace&object={object}-creator&relation=member&subject=default-namespace:fast-dev-director#member
I belive that userset rewrites
would probably allow for a more complete (H)RBAC implementation as described in https://www.usenix.org/system/files/atc19-pang.pdf
I think you are right, revoking a permission for a role does require iterating. But this could either be done by the high level RBAC solution proposed here, or enabling bulk deletion by providing partial relation tuples. I will open another issue for that idea, as it might be useful for other cases as well (think "purge user/object from everywhere").
Bulk deletion could be a working solution, but then again there would be no solution for the opposite operation: addition.
In RBAC I must have a way to both add and delete permissions from a role. Ideally this operation shouldn't require touching thousands or million of tuples as performance and consistency would probably be hard to guarantee.
It is my understanding that if user rewrites were available no high-level RBAC API would be required, am I correct? Would RBAC work out of the box with user rewrites?
Actually, I think we can circumvent that limitation right now, if we fully ditch the object id. So basically everything from the blog post, but remove the first step. In this case it would not be related to specific objects anymore, but the permission itself is the object. You only have the following relation tuples:
create#execute@it-director#member
view#execute@it-director#member
delete#execute@it-director#member
view#execute@dev-director#member
it-director#member@Hermes
dev-director#member@Fry
dev-director#member@Bender
Revoking the create permission for all it-director
s would translate to deleting the first tuple. Adding a new one is also just one tuple.
But again, this does not work on a object level, but for the whole system. I am not sure how easy this could be implemented by the Keto client :thinking: Would this just boil down to forwarding the operation that is made, e.g. the HTTP method for a REST service? The user is then either allowed to view
all or no object.
I now finally fully understand the problem you have @christian-roggia :sweat_smile: What I don't understand is do you want to add a new permissions to all objects in the system, or to a subset (might be large)? How would you solve the later case with other authorization systems? Because if it is not a global view
permission, then you would always have to somehow specify the list of all objects, right? If it is global, does my above idea work, or why not?
What I've found to be the easiest to reason about the individual person membership was not to explicitly make the individual persons a member. If I know that Bender is a dev-director
, I can fetch every role of Bender and check the resource against Bender's roles. I think this is in the principle how the Zanzibar usersets would work.
I mean, this mapping has to exist somewhere. Otherwise how do you know the user has certain mapping assigned. There's no magical method to have a mapping without the info in the fact storage.
This boils down to a cross reference: for any role Bender holds, is any of these roles allowed to execute this action on an object. The higher level tool is definitely able to update this consistently across large number of objects. I also think it's not uncommon to expect this kind of update to happen over millions of objects. You'll have to have the trade off somewhere: either when inserting / updating the permissions or during the query. I'd expect the trade off to be on the create / update step because the query must be as fast as possible.
Just a terminology thing, a userset
in the paper is the part where you have
obj#rel@other-obj#rel
^-----------^ userset
So instead of a single user, you refer to a set of users defined by "everybody who has this permission". This part is already implemented in Keto, but we are missing the "rewrites", which allow you to basically define relation tuples globally with kind of variables. One good example is indirect permission on a file through permission on the parent directory. Currently you have to explicitly say that file1#view@parent_dir1#view
but this should be defined globally by saying that you can view every file whose parent you can view. That part is not implemented yet.
Therefore, you don't have to get all the roles of a specific user to check if any of the roles is allowed to do something, but you can instead if you have these tuples:
create#execute@it-director#member
view#execute@it-director#member
delete#execute@it-director#member
view#execute@dev-director#member
it-director#member@Hermes
dev-director#member@Fry
dev-director#member@Bender
check if create#execute@Bender
Thanks, that does explain things better. The higher level tool would definitely make things easier as thinking in tuples over multiple relationships is a bit of a mind bender. But then, this isn’t a Keto (Zanzibar) problem, rather a business domain complexity.
The higher level tool would definitely make things easier as thinking in tuples over multiple relationships is a bit of a mind bender.
Haha my brain thinks only in tuples since I worked so much on Keto :joy:
But then, this isn’t a Keto (Zanzibar) problem, rather a business domain complexity.
This is a good point, we have to decide where the line between Keto and your business logic is. It seems that there are some steps that are always required for RBAC, and in that case it does make sense to add a high level abstraction for that. It would just be a shortcut and all the decisions will still be made through the ACLs.
Hey @zepatrik, there is another problem with the queries:
curl --silent 'http://localhost:4466/expand?namespace=default-namespace&subject=Fry&relation=member&max-depth=2' | jq '.'
returns
{
"type": "union",
"subject": "default-namespace:#member",
"children": [
{
"type": "leaf",
"subject": "Bender"
},
{
"type": "leaf",
"subject": "Fry"
},
{
"type": "leaf",
"subject": "Hermes"
},
{
"type": "leaf",
"subject": "default-namespace:it-director#member"
},
{
"type": "leaf",
"subject": "default-namespace:it-director#member"
},
{
"type": "leaf",
"subject": "default-namespace:dev-director#member"
},
{
"type": "leaf",
"subject": "default-namespace:it-director#member"
}
]
}
this is completely not what the logic suggests the result should be.
The following is a complete model that we previously implemented through a graph database (dgraph) that fully supported RBAC:
This is an example of the inheritance of policies from the most generic to the most granular.
NOTE: Policies can be global or attached to a specific object.
What I would expect from an RBAC implementation is the following:
I have 3 users in a Google Cloud project called [PROJECT-X]
:
Christian
is the project administrator and has the role admin
assigned to the project itself, this role is a global role and is therefore inherited by all resources managed by the project.
The role admin
grants the following permissions:
The project owns a Pub/Sub Topic
resource called [TOPIC-A]
.
Being [TOPIC-A]
a child of [PROJECT-X]
, it inherits all policies from it, and therefore the administrator Christian
is authorized to get
, create
, and delete
the topic itself.
Additionally, the user Matteo
has been assigned the role pubsub-editor
to the [TOPIC-A]
.
The role pubsub-editor
grants the following permissions:
Matteo
and Christian
have therefore administrative authority on [TOPIC-A]
, Matteo
has the role assigned directly, while Christian
has the role inherited.
Finally, the project owns a Storage Bucket
resource called [BUCKET-W]
.
Being [BUCKET-W]
a child of [PROJECT-X]
, it inherits all policies from it, and therefore the administrator Christian
is authorized to get
, create
, and delete
the bucket itself.
Additionally, all users have been granted read-only access to the [BUCKET-W]
through the role storage-viewer
.
The role storage-viewer
grants the following permissions:
Matteo
, Christian
, and Thomas
have all view access on the bucket (and its content), while only Christian
has administration access on it since the role admin
granted the additional permissions storage.object.create
and storage.object.delete
.
After an accident, it is decided that members of the pubsub-editor
role should no longer have pubsub.topic.delete
permission.
It is also observed that Matteo
is able to manage the Pub/Sub resource but it isn't able to publish any message, it is decided then that the pubsub-editor
should be granted the additional pubsub.topic.publish
permission.
The role is therefore changed to reflect the new decision:
This change must be reflected globally and retroactively.
Hello contributors!
I am marking this issue as stale as it has not received any engagement from the community or maintainers a year. That does not imply that the issue has no merit! If you feel strongly about this issue
Throughout its lifetime, Ory has received over 10.000 issues and PRs. To sustain that growth, we need to prioritize and focus on issues that are important to the community. A good indication of importance, and thus priority, is activity on a topic.
Unfortunately, burnout has become a topic of concern amongst open-source projects.
It can lead to severe personal and health issues as well as opening catastrophic attack vectors.
The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone.
If this issue was marked as stale erroneous you can exempt it by adding the backlog
label, assigning someone, or setting a milestone for it.
Thank you for your understanding and to anyone who participated in the conversation! And as written above, please do participate in the conversation if this topic is important to you!
Thank you 🙏✌️
Is there an anticipated timeline for when native RBAC support will go live? :)
Truly native support as tracked in this issue is currently not being worked on actively. In general, we can't give out timelines for open source issues and features, all I can say is that we currently don't work on it and won't start tomorrow, especially because #877 is a per-requesit.
@zepatrik any update on this now that #877 has been merged in?
As I said, truly native support as tracked in this issue is currently not being worked on actively. Maybe you want to add your use-case and solution proposal?
Hi, maybe I am wrong but isn't the ory permission language
(official doc) the solution to this, since the TS file allows full customization of what is accessible by what?
More specifically what is missing for (H) RBAC to be usable?
Yes, you can build RBAC using OPL, and configure it as needed. This issue is rather about having an "official" way to do RBAC. With the OPL, the new option of using dependencies and building authz libraries opened up. Instead of everyone defining their own (probably very similar) flavor of RBAC, we could provide something similar to
import { Namespace } from "@ory/keto-namespace-types"
import { Role } from "@ory/rbac"
class User implements Namespace {...}
class File implements Namespace {...}
class FileRole extends Role {
// possibility to customize the role behavior
}
But maybe this also is too much complexity and it makes more sense to just have everyone implement their own RBAC? Maybe we could collect some definitions here to see if it makes sense?
@zepatrik
Maybe we could collect some definitions here to see if it makes sense?
It would be very nice to have a complete (and more complex, real life use case) RBAC-like example in the examples folder of this repo with best practices / common models.
I think it would greatly benefit users coming into Keto like me
Hello contributors!
I am marking this issue as stale as it has not received any engagement from the community or maintainers for a year. That does not imply that the issue has no merit! If you feel strongly about this issue
Throughout its lifetime, Ory has received over 10.000 issues and PRs. To sustain that growth, we need to prioritize and focus on issues that are important to the community. A good indication of importance, and thus priority, is activity on a topic.
Unfortunately, burnout has become a topic of concern amongst open-source projects.
It can lead to severe personal and health issues as well as opening catastrophic attack vectors.
The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone.
If this issue was marked as stale erroneously you can exempt it by adding the backlog
label, assigning someone, or setting a milestone for it.
Thank you for your understanding and to anyone who participated in the conversation! And as written above, please do participate in the conversation if this topic is important to you!
Thank you 🙏✌️
Is your feature request related to a problem? Please describe.
Implementing RBAC currently is possible, but requires a lot of thought and workarounds. A current solution is described in https://gruchalski.com/posts/2021-05-15-rbac-with-ory-keto/
Describe the solution you'd like
From that post:
Describe alternatives you've considered
cc @radekg @christian-roggia