Giter Club home page Giter Club logo

Comments (18)

iantanwx avatar iantanwx commented on May 16, 2024 3

I was wondering about this problem too. The issue is that we the route may be dynamic. For example, we may want a user to only be able to view his own purchases. Consider:

GET /orders/:id

We don't want non-owners retrieving arbitrary orders, so we must check if this order is owned by the requesting user based on his token.

If you use a file based policy, there is currently no way to protect such a route unless you literally fill in every user in your system. Either that, or authorise in the route handler, which in my opinion is a bad idea.

The only viable option I can think of is to assign each user a unique role with corresponding unique permissions. I would then stuff the generated policy into a redis instance for fast retrieval and/or caching.

from casbin.

hsluoyz avatar hsluoyz commented on May 16, 2024 2

https://casbin.org/docs/en/function#how-to-add-a-customized-function

from casbin.

hsluoyz avatar hsluoyz commented on May 16, 2024 1

@iantanwx ,

For your case, I think a way to handle access control is:

  1. define a function to retrieve :id value from /orders/:id via regex, like getID(r.obj, p.obj)
  2. define a function to compare the user with the owner of the order, like ownOrder(r.sub, order_id), I suppose you will have an order struct, which has a owner property. So you can find the order with order_id, then compare user with the order's owner in the function.
  3. At last, your Casbin matcher looks like: m = ownOrder(r.sub, getID(r.obj, p.obj)) && regexMatch(r.act, p.act)

In this way, you won't need to store every mapping from an individual user to an order. So the policy rules can be limited in a reasonable number.

from casbin.

hsluoyz avatar hsluoyz commented on May 16, 2024 1

Then I think you don't need to use the g() function.

Model:

[request_definition]
r = sub, obj, act, role

[policy_definition]
p = obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (r.role == "admin" || isOwner(r.sub, r.obj, p.obj)) && regexMatch(r.act, p.act)

An example policy is:

p, /users, (GET)|(POST)
p, /users/:id, (POST)|(GET)|(PUT)|(DELETE)

from casbin.

hsluoyz avatar hsluoyz commented on May 16, 2024 1

@acim please open a new issue.

from casbin.

hsluoyz avatar hsluoyz commented on May 16, 2024

Casbin usually acts as a permission checker for all routes. See the web framework middlewares: https://github.com/casbin/casbin#web-frameworks

You can control access on a per route basis. Please refer to the RESTful example at: https://github.com/casbin/casbin#examples

The corresponding policy rule for your case would be:

p, alice, /posts, POST

So the user named alice will be allowed to send POST requests to /posts.

from casbin.

iantanwx avatar iantanwx commented on May 16, 2024

I am not sure if there is a better practice for this @hsluoyz. There will be many permissions in the database as the system grows and reads in such an enormous table might become very very slow which could mean very slow server boot time.

from casbin.

iantanwx avatar iantanwx commented on May 16, 2024

@hsluoyz thanks.

This seems to be a more scalable way. If I would like to also use RBAC, I would have to add

m = (g(r.obj, p.obj) || isOwner(r.sub, getID(r.obj, p.obj))) && regexMatch(r.act, p.act)?

Assuming the policy looks something like

p, some/route/:id, GET
g, some/route/:id, admin

So that in this case, some/route/:id would only be accessible to: admin role, or owner.

from casbin.

hsluoyz avatar hsluoyz commented on May 16, 2024

I don't think you are understanding the role function correctly. g, some/route/:id, admin really means some/route/:id has the role admin. So every permission assignment (aka p policy) that works on admin will also work on some/route/:id. In this case, you don't have something like p, admin, GET.

How is the admin role defined? g(r.obj, p.obj) really means the URL path in request has the role of URL path in policy. Is this correct? Or what you refers to is really the role property for a user?

from casbin.

iantanwx avatar iantanwx commented on May 16, 2024

Sorry, I made a mistake.

Example model:

[request_definition]
r = sub, obj, act, role

[policy_definition]
p = obj, act

[role_definition]
g = _, _, # resource, role

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (g(r.obj, r.role) || isOwner(r.sub, r.obj, p.obj)) && regexMatch(r.act, p.act)

Example policy:

p, /users, (GET)|(POST)
p, /users/:id, (POST)|(GET)|(PUT)|(DELETE)

g, /users, admin
g, /users/:id, admin

Edit: in this case, I would store role on the user's token. I would then pass the role to the matcher in my middleware, with the user id.

Edit2: Combine the functions you suggested into one isOwner function that takes all the necessary parameters and checks the ownership of the resource requested.

Edit3: In this case, a normal user would never be able to access /users. But a user would be able to access /users/:id if he is owner of that user (i.e. himself).

from casbin.

hsluoyz avatar hsluoyz commented on May 16, 2024

I'm even confused with the above model. You said:

some/route/:id would only be accessible to: admin role, or owner.

I believe it means that the subject (r.sub) can choose to have an admin role or not, or be an owner. But g(r.obj, r.role) really means to determine whether the object (URL path) has a role. Also you pass in a role in the request. I think this is unnecessary. Here's what I think would be right:

Model:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (g(r.sub, p.sub) || isOwner(r.sub, getID(r.obj, p.obj))) && regexMatch(r.act, p.act)

An example policy is:

p, admin, some/route/:id, GET
p, *, another/route/:id, GET
g, alice, admin

from casbin.

iantanwx avatar iantanwx commented on May 16, 2024

I misunderstood how g(...) works.

In this example it means I have to add all users and their roles to the policy when my app boots up. What I was trying to achieve is to determine:

a. If user's token shows he's admin, allow him to access route.
b. If not, check if he is owner

In other words, this means we want to avoid adding any users to groups. We just want to say:

/some/route/:id is accessible to admins and owners

from casbin.

iantanwx avatar iantanwx commented on May 16, 2024

Thanks, that seems to solve my problem. Good work!

from casbin.

jmwohl avatar jmwohl commented on May 16, 2024

@iantanwx I have a need for something similar. The info on this issue thread has pointed me in the right direction, but I'd love to see an example of the implementation of your solution.

from casbin.

nitinkhosla79 avatar nitinkhosla79 commented on May 16, 2024

@iantanwx @hsluoyz Is there an example to see isOwner implementation? Or documentation on how to write custom functions? I am using python.

from casbin.

hsluoyz avatar hsluoyz commented on May 16, 2024

@nitinkhosla79 isOwner is a custom function defined by yourself.

from casbin.

nitinkhosla79 avatar nitinkhosla79 commented on May 16, 2024

how does model/conf file know that where is this function defined? Please share any documentation on custom function.
.
Our scenario: A request is sent from firm A to firm B. (example private buyer and seller deal) : that deal should only be viewed by sender and receiver. New deals can be created anytime. How to express this in configuration?

[request_definition]
r = sub, obj, act, role

[policy_definition]
p = obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = (r.role == ("sender" | "receiver")  && regexMatch(r.act, p.act)
p, /users, (GET)|(POST)
p, /users/:id, (POST)|(GET)|(PUT)|(DELETE)

from casbin.

acim avatar acim commented on May 16, 2024

If this custom function isOwner has to access database to find out the owner, does it mean we have to access the same database record (document) again if Casbin permits access? I mean is there some way to avoid this redundant read? I thought to use Casbin as middleware (Golang).

from casbin.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.