Change permission system to casbin #1834

Closed
opened 2026-02-05 02:01:01 +03:00 by OVERLORD · 2 comments
Owner

Originally created by @gjvnq on GitHub (Sep 1, 2020).

This is more of a suggestion for a far future than a feature request.

I think that by using Casbin it would be possible to kill multiple birds with one stone:

  • Add support for separate domains/realms within a single installation.
  • Auto inherit permission for books in multiple shelves.
  • Add users as their own entity in book and shelf ACLs. (no longer single user role hack)
  • Easily transfer books and shelves between users.
  • Store permissions in different places without much code change. (Example: MySQL and LDAP)

From my experience, using casbin to do all the "dirty work" was one of the best decisions I made on a project of mine. The main downside is understanding how to write policy modules, so here is what I used and I think would work for Bookshelf (except for the domains part):

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _
g2 = _, _
g3 = _, _

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

[matchers]
m = g(r.sub, p.sub) && (r.obj == "?" || p.obj == "*" || g2(r.obj, p.obj)) && (r.act == "?" || p.act == "*" || g3(r.act, p.act))

This allows for inheritance not only on users and groups, but also on permissions themselves. For example, the rule g3, read, export means that whoever has the permission export also has read but not the other way around.

By the way g is the grouping rule for users, g2 for objects and g3 for permissions. The * means all and the ? means any. So calling my_enforcer.enforce('u1', '?', 'read') will return true if there is at least one object for which user 1 is allowed to read. And having a policy like p, u1, *, read means that user 1 can read all objects.

Given that supporting multiple domains/tenants would be nice, here is what the policy model probably would be: (I haven't tested it)

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

[policy_definition]
p = dom, sub, obj, act

[role_definition]
g = _, _, _
g2 = _, _, _
g3 = _, _, _

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

[matchers]
m = r.dom == p.dom && g(r.sub, p.sub, r.dom) && (r.obj == "?" || p.obj == "*" || g2(r.obj, p.obj, r.dom)) && (r.act == "?" || p.act == "*" || g3(r.act, p.act, r.dom))
Originally created by @gjvnq on GitHub (Sep 1, 2020). This is more of a suggestion for a far future than a feature request. I think that by using [Casbin](https://casbin.org/) it would be possible to kill multiple birds with one stone: * Add support for separate domains/realms within a single installation. * Auto inherit permission for books in multiple shelves. * Add users as their own entity in book and shelf ACLs. (no longer single user role hack) * Easily transfer books and shelves between users. * Store permissions in different places without much code change. (Example: MySQL and LDAP) From my experience, using casbin to do all the "dirty work" was one of the best decisions I made on a project of mine. The main downside is understanding how to write policy modules, so here is what I used and I think would work for Bookshelf (except for the domains part): ``` [request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ g2 = _, _ g3 = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && (r.obj == "?" || p.obj == "*" || g2(r.obj, p.obj)) && (r.act == "?" || p.act == "*" || g3(r.act, p.act)) ``` This allows for inheritance not only on users and groups, but also on permissions themselves. For example, the rule ```g3, read, export``` means that whoever has the permission ```export``` also has ```read``` but not the other way around. By the way ``g`` is the grouping rule for users, ``g2`` for objects and ``g3`` for permissions. The ``*`` means all and the ``?`` means any. So calling ``my_enforcer.enforce('u1', '?', 'read')`` will return true if there is at least one object for which user 1 is allowed to read. And having a policy like ``p, u1, *, read`` means that user 1 can read all objects. Given that supporting multiple domains/tenants would be nice, here is what the policy model probably would be: (I haven't tested it) ``` [request_definition] r = dom, sub, obj, act [policy_definition] p = dom, sub, obj, act [role_definition] g = _, _, _ g2 = _, _, _ g3 = _, _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.dom == p.dom && g(r.sub, p.sub, r.dom) && (r.obj == "?" || p.obj == "*" || g2(r.obj, p.obj, r.dom)) && (r.act == "?" || p.act == "*" || g3(r.act, p.act, r.dom)) ```
Author
Owner

@ssddanbrown commented on GitHub (Sep 5, 2020):

Hi @gjvnq,
Thanks for the proposal.

I'll be honest, I'm immediately put-off when seeing the above syntax. None of that is immediately understandable. I think it'll take a lot of understanding to see how that maps to the current BookStack permission.

Overall, I'd be concerned to rip-out the current system and rely on a third-party system for what is a core part, heavily tied in to the project. It's a heavy "Stone" for the listed "Birds" we'd be killing with it.

That said, I'll try to keep an open mind for the moment as, since you say, may be something to consider for the far future.

How does this system handle permissions on lists/queries? Checking permissions on a single item has always been relatively easy but I've always found complications to appear when attempting to efficiently check permissions when performing listing/query operations? For example, when a user opens a book in BookStack they'd see only the pages and chapters within that they have view permissions for. How would casbin handle that? Can the permissions be considered in the queries we make?

From my view, many of intended features this would cover would really just need some ui/process/table updates outside of the permission system or some logical re-thinking. It's not solely the current back-end permission system that's preventing implementation of those features.

@ssddanbrown commented on GitHub (Sep 5, 2020): Hi @gjvnq, Thanks for the proposal. I'll be honest, I'm immediately put-off when seeing the above syntax. None of that is immediately understandable. I think it'll take a lot of understanding to see how that maps to the current BookStack permission. Overall, I'd be concerned to rip-out the current system and rely on a third-party system for what is a core part, heavily tied in to the project. It's a heavy "Stone" for the listed "Birds" we'd be killing with it. That said, I'll try to keep an open mind for the moment as, since you say, may be something to consider for the far future. How does this system handle permissions on lists/queries? Checking permissions on a single item has always been relatively easy but I've always found complications to appear when attempting to efficiently check permissions when performing listing/query operations? For example, when a user opens a book in BookStack they'd see only the pages and chapters within that they have view permissions for. How would casbin handle that? Can the permissions be considered in the queries we make? From my view, many of intended features this would cover would really just need some ui/process/table updates outside of the permission system or some logical re-thinking. It's not solely the current back-end permission system that's preventing implementation of those features.
Author
Owner

@ssddanbrown commented on GitHub (Jan 26, 2021):

Since there's been no follow-up or further discussion I'm going to close this.

@ssddanbrown commented on GitHub (Jan 26, 2021): Since there's been no follow-up or further discussion I'm going to close this.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/BookStack#1834