Skip to content
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

High level RBAC API/manager #598

Closed
zepatrik opened this issue May 17, 2021 · 20 comments
Closed

High level RBAC API/manager #598

zepatrik opened this issue May 17, 2021 · 20 comments
Labels
feat New feature or request. rfc A request for comments to discuss and share ideas. stale Feedback from one or more authors is required to proceed.

Comments

@zepatrik
Copy link
Member

zepatrik commented May 17, 2021

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:

If there was a higher level system responsible for actual role management, the tool could present a role object called IT Director with these granular tuples hidden from view. The role management operator would be working with that abstraction instead of these granular items.

Describe alternatives you've considered

  1. keep as is: con RBAC is pretty standard, so should be supported somehow
  2. offload to other service: not having RBAC as part of the core functionality but instead a separate "RBAC manager" that wraps around Keto

cc @radekg @christian-roggia

@zepatrik zepatrik added feat New feature or request. rfc A request for comments to discuss and share ideas. labels May 17, 2021
@christian-roggia
Copy link

christian-roggia commented May 17, 2021

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

Or did I actually miss something?

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

@zepatrik
Copy link
Member Author

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").

@christian-roggia
Copy link

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?

@zepatrik
Copy link
Member Author

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-directors 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 🤔 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 😅 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?

@radekg
Copy link
Contributor

radekg commented May 17, 2021

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.

@zepatrik
Copy link
Member Author

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

@radekg
Copy link
Contributor

radekg commented May 17, 2021

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.

@zepatrik
Copy link
Member Author

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 😂

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.

@radekg
Copy link
Contributor

radekg commented May 17, 2021

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.

@christian-roggia
Copy link

Abstract implementation

The following is a complete model that we previously implemented through a graph database (dgraph) that fully supported RBAC:

AuthZ - Policies - GraphQL

  1. Every object might have a policy attached to it that defines who is authorized to do what.
  2. A policy is composed of multiple bindings that connect a single role with a group of members, which could be direct users or groups of users.
  3. Every role is globally defined and contains a set of permissions.
  4. Every object might have a parent (and only one) from which it inherits policies.

AuthZ - Hierarchy - GraphQL

This is an example of the inheritance of policies from the most generic to the most granular.

AuthZ - Graph - GraphQL

NOTE: Policies can be global or attached to a specific object.

Hard requirements

What I would expect from an RBAC implementation is the following:

  • Revoking or granting new permissions to a role is an atomic operation (I don't have to touch all objects bound to that role)
  • I can assign a role to an object or enforce it globally, this can easily be implemented with an abstract "root" object
  • Roles are global sets of permissions and I don't have to create or allocate a new one every time I need to create a binding
  • It must be taken into account that the same role could be assigned to hundreds of thousands of different objects
  • Inheritance must be possible across resources

Practical examples

I have 3 users in a Google Cloud project called [PROJECT-X]:

  • Matteo
  • Christian
  • Thomas

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:

  • pubsub.topic.get
  • pubsub.topic.create
  • pubsub.topic.delete
  • pubsub.topic.publish
  • storage.object.get
  • storage.object.create
  • storage.object.delete

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:

  • pubsub.topic.get
  • pubsub.topic.create
  • pubsub.topic.delete

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:

  • storage.object.get

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:

  • pubsub.topic.get
  • pubsub.topic.create
  • pubsub.topic.publish

This change must be reflected globally and retroactively.

@github-actions
Copy link

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

  • open a PR referencing and resolving the issue;
  • leave a comment on it and discuss ideas how you could contribute towards resolving it;
  • leave a comment and describe in detail why this issue is critical for your use case;
  • open a new issue with updated details and a plan on resolving the 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 🙏✌️

@github-actions github-actions bot added the stale Feedback from one or more authors is required to proceed. label May 21, 2022
@zepatrik zepatrik removed the stale Feedback from one or more authors is required to proceed. label May 23, 2022
@timonbimon
Copy link

Is there an anticipated timeline for when native RBAC support will go live? :)

@aeneasr
Copy link
Member

aeneasr commented Jun 2, 2022

#877 ;)

@zepatrik
Copy link
Member Author

zepatrik commented Jun 3, 2022

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.

@cbrendanprice
Copy link

@zepatrik any update on this now that #877 has been merged in?

@zepatrik
Copy link
Member Author

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?

@nbrugger-tgm
Copy link

nbrugger-tgm commented Oct 30, 2022

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?

@zepatrik
Copy link
Member Author

zepatrik commented Nov 2, 2022

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?

@Davincible
Copy link

@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

Copy link

github-actions bot commented Feb 6, 2024

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

  • open a PR referencing and resolving the issue;
  • leave a comment on it and discuss ideas on how you could contribute towards resolving it;
  • leave a comment and describe in detail why this issue is critical for your use case;
  • open a new issue with updated details and a plan for resolving the 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 🙏✌️

@github-actions github-actions bot added the stale Feedback from one or more authors is required to proceed. label Feb 6, 2024
@github-actions github-actions bot closed this as completed Mar 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat New feature or request. rfc A request for comments to discuss and share ideas. stale Feedback from one or more authors is required to proceed.
Projects
None yet
Development

No branches or pull requests

8 participants