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

Built-in get_allowed_permissions-style method to retrieve all allowed (action, resource) pairs for a given actor #1098

Open
asyncee opened this issue Aug 24, 2021 · 7 comments

Comments

@asyncee
Copy link

asyncee commented Aug 24, 2021

Hello!

We are creating an RBAC implementation based on OSO and it is working fine.

Currently the library provides a way to retrieve actions for a single resource using oso.get_allowed_actions().

For example, John can "read" a "Post 1". By asking oso.get_allowed_actions("Josh", "Post 1") we can get following result: ["read"].

Unfortunately, our project has a complex UI so it is not possible to ask backend for available actions per resource on the page because it may cause:

  1. high amount of similar http requests asking about allowed actions for resource
  2. high traffic
  3. resource consumption on the server
  4. poor user experience and other related issues

Is it possible to somehow get result like [("read", "Post 1")] in a single query? We are trying to feed UI with all available permissions using one HTTP query.

We managed to get almost desired results using following python code:

# Predicates have following signature:
# assign_role(user, role) — maps user to role
# role_allow(role, action, resource, user) — describes which action role's owner can do, user is a hacky way to provide more context to the predicate.
# User -assigned_to-> Role -grants-> Action-On-Resource 

user = User(2)
assigned_roles = oso.query_rule("assign_role", user, Variable("role"))
for result in assigned_roles:
    role = result.get("bindings").get("role")
    permissions = oso.query_rule("role_allow", role, Variable("action"), Variable("resource"), user, accept_expression=True)
    for p in permissions:
        print(p)

    # The output is following:
        # {'bindings': {'resource': 'Post1', 'action': 'read'}, 'trace': None}
        # {'bindings': {'action': 'delete', 'resource': Expression(And, [Expression(Isa, [Variable('_this'), Pattern(Post, {})]), Expression(Unify, [Expression(Dot, [Variable('_this'), 'id']), 2])])}, 'trace': None}
        # {'bindings': {'resource': 'Post2', 'action': 'write'}, 'trace': None}

Provided solution

  • feels like a hack,
  • has N+1 query problem (caching must be used to partially solve the issue),
  • we have to parse returned Expression to figure out which instance (object) is described in results

It would be great to have a way to achieve this task in more readable and performant way.

Thanks!

@gj
Copy link
Member

gj commented Aug 25, 2021

Thanks for opening the issue @asyncee! I agree this would be a nice feature to have — I'm imagining an API in the same vein as Oso.get_allowed_actions (renamed to Enforcer.authorized_actions in the new beta release).

It should be possible to get around the N+1 issue by setting up your policy like so:

has_role("Abhi", "writer");

role_allow("writer", "write", "Post 1");
role_allow("writer", "write", "Post 2");

allow(user, action, resource) if
  has_role(user, role) and
  role_allow(role, action, resource);

And then querying for:

abhi = "Abhi"
permissions = oso.query_rule("allow", abhi, Variable("action"), Variable("resource"), accept_expression=True)
for p in permissions:
    print(p)

@asyncee
Copy link
Author

asyncee commented Aug 25, 2021

Great news, thanks, i'll try it!

@asyncee
Copy link
Author

asyncee commented Aug 26, 2021

It works great, not sure why this elegant solution missed my head :)

@gj
Copy link
Member

gj commented Aug 26, 2021

It works great, not sure why this elegant solution missed my head :)

In fairness, it also missed mine while I was talking to you on Slack before you opened this issue haha. I re-read the issue and had an "aha" moment 😆

FWIW I still think we could ultimately add a built-in get_allowed_actions-style API for this — Polar is very well-suited to this "for a given rule, keep N arguments concrete and query for all possible combinations of the remaining arguments" type of policy introspection.

@gj gj changed the title A way to retrieve actions with objects in bulk Built-in get_allowed_permissions-style method to retrieve all allowed (action, resource) pairs for a given actor Aug 26, 2021
@gj
Copy link
Member

gj commented Aug 26, 2021

@asyncee does the new issue title look good? Feel free to adjust

@asyncee
Copy link
Author

asyncee commented Aug 27, 2021

Very accurate, looks good.

@kkirsche
Copy link
Contributor

kkirsche commented Feb 2, 2023

This seems like, from an implementation perspective, it would pair well with #1427 as a pull request in one go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants