Casbin Forum runs on Casnode, the open-source Discourse-style forum. Try the Casnode demo →
Casbin Forum

Finally:

p, 1, /something, GET, sol-*

Why if there are zero groups does it pass. There should be nothing to match right?
But if I do:

p, r1, /something, GET, sol-*

It again gives false as it should be

I found something strange, here is my model (I stripped all the fluff):

[request_definition]
r = user, path, method, tag

[policy_definition]
p = role, path, method, tag

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

[role_definition]
g = _, _, _

[matchers]
m = g(r.user, p.role, r.tag)

Using the following policy:

p, 1, /something, GET, sol-*
g, 2, 1, sol-1
g, 3, 1, sol-1

And my request:

1, /something, GET, sol-1

I get true even though I expect it to be false

If I change my policy:

p, r1, /something, GET, sol-*
g, 2, r1, sol-1
g, 3, r1, sol-1

Then in results into false as it should

Why is this the case?

Is it best practice to prefix ids if those are used in the policy?

You can test it in the editor

Yes I believe they do but generally you can also just load it one time and keep it in memory

I want to understand how it will integrate with my application.

I will check it out

Is there a free trial or hobby project version that can be used?

Not the most efficient but gaurantee that I get the latest data

Yeah currently not worrying about perfomance

I am not using the default structure like in this adapter:
https://github.com/casbin/xorm-adapter

So I can gather the data I need efficiently and generate the policy + role group at runtime

My example is just an example to make it understandable. In the end it will all just be id's that are matched

In my case only the model is static all others things are loaded from db or cache at runtime per request

Yeah same

customers can create their own roles with their own permissions

I went from this model:

const stringModel = `
[request_definition]
r = user, dom, obj, act, tag

[policy_definition]
p = role, dom, obj, act, tag

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

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

[matchers]
m = g(r.user, p.role, r.tag) && g2(p.role, r.dom) && keyMatch(r.tag, p.tag) && keyMatch(r.act, p.act) && (keyMatch(r.obj, p.obj) || keyMatch2(r.obj, p.obj))
    `

func TestDefaultRbac(t *testing.T) {
    m, _ := model.NewModelFromString(stringModel)
    e, _ := casbin.NewEnforcer(m, false)

    e.AddPolicies([][]string{
        {"reader", "org1", "/project", "GET", "project_*"},
        {"editor", "org1", "/project", "DELETE", "project_*"},
        {"reader2", "org2", "/project", "GET", "project_*"},
    })

    e.AddNamedGroupingPolicies("g", [][]string{
        {"tom", "reader", "project_1"},
        {"tom", "reader", "project_2"},
        {"tom", "reader", "project_3"},
        {"tom", "editor", "project_5"},
        {"henk", "reader2", "project_6"},
    })

    e.AddNamedGroupingPolicies("g2", [][]string{
        {"reader", "org1"},
        {"reader2", "org2"},
        {"editor", "org1"},
    })

    enforcer := func(user, dom, obj, act, tag string, expect bool) {
        resp, _ := e.Enforce(user, dom, obj, act, tag)
        if resp != expect {
            t.Errorf("Response is for %s %s %s %s %s %t expected to be %t", user, dom, obj, act, tag, resp, expect)
        }
    }

    enforcer("tom", "org1", "/project", "GET", "project_1", true)
    enforcer("tom", "org1", "/project", "DELETE", "project_1", false)
    enforcer("tom", "org1", "/project", "DELETE", "project_5", true)
    enforcer("tom", "org2", "/project", "GET", "project_6", false)
    enforcer("henk", "org2", "/project", "GET", "project_6", true)
}

to this, I left out the whole org part since the roles will be unique per org and are therefore implicity tied to an org:


const stringModel2 = `
[request_definition]
r = user, obj, act, tag

[policy_definition]
p = role, obj, act, tag

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

[role_definition]
g = _, _, _

[matchers]
m = g(r.user, p.role, r.tag) && keyMatch(r.tag, p.tag) && keyMatch(r.act, p.act) && (keyMatch(r.obj, p.obj) || keyMatch2(r.obj, p.obj))
    `

func TestDefaultRbac2(t *testing.T) {
    m, _ := model.NewModelFromString(stringModel2)
    e, _ := casbin.NewEnforcer(m, false)

    e.AddPolicies([][]string{
        {"reader", "/project", "GET", "project_*"},
        {"editor", "/project", "DELETE", "project_*"},
        {"reader2", "/project", "GET", "project_*"},
    })

    e.AddNamedGroupingPolicies("g", [][]string{
        {"tom", "reader", "project_1"},
        {"tom", "reader", "project_2"},
        {"tom", "reader", "project_3"},
        {"tom", "editor", "project_5"},
        {"henk", "reader2", "project_6"},
    })

    enforcer := func(user, obj, act, tag string, expect bool) {
        resp, _ := e.Enforce(user, obj, act, tag)
        if resp != expect {
            t.Errorf("Response is for %s %s %s %s %t expected to be %t", user, obj, act, tag, resp, expect)
        }
    }

    enforcer("tom", "/project", "GET", "project_1", true)
    enforcer("tom", "/project", "DELETE", "project_1", false)
    enforcer("tom", "/project", "DELETE", "project_5", true)
    enforcer("tom", "/project", "GET", "project_6", false)
    enforcer("henk", "/project", "GET", "project_6", true)
}

Both do the same thing one is explicit with role + org combination and the other is implicit. E.g. reader is part of org1 and reader2 is part of org2

I plan to make a custom adapter https://casbin.org/docs/adapters/ that loads the filtered policy for each request and checks it.

that loads the filtered policy for each request and checks it. for endpoints that need it

Anyone know if it is more efficient to have many role_definiton mappings or is it better to have many policy_definiton rules

So whats better:

g _, _, _
user,1 role_1, project_1

user1000, role_1, project_1

Or many policy definitons

policy_definitions:
r = sub, dom, act
user1, project1, read
...
user1000, project1, read

I can implement my usecase with both options. But from some benchmarks it seems that having many role_defintion mappings is more efficient.

To add some numbers to this: there can be 20000 role user mappings and just few lines of policy definitions

I also found this so that seems to verify what I found:
Grant permissions to RBAC roles instead of users directly. Casbin's RBAC is implemented by a role inheritance tree (as a cache). So, given a user like Alice, Casbin only takes O(1) time to query the RBAC tree for the role-user relationship and perform enforcement. If your g rules don't change often, then the RBAC tree won't need to be constantly updated. See the details of this discussion here: https://github.com/casbin/casbin/issues/681#issuecomment-763801583