From bbd96fafa06700cf5803eab538ba26db6bf96a56 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:56:21 +0000 Subject: [PATCH 1/4] Initial plan From be6ee4cee11d529b34658ef297f7d7de9be27bc3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:06:05 +0000 Subject: [PATCH 2/4] Add RBAC with resource scope implementation and tests Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- examples/RBAC_WITH_RESOURCE_SCOPE.md | 206 ++++++++++++ ..._with_resource_scope_and_tenant_model.conf | 14 + ..._with_resource_scope_and_tenant_policy.csv | 7 + examples/rbac_with_resource_scope_model.conf | 14 + ...ith_resource_scope_multitenancy_model.conf | 14 + ...ith_resource_scope_multitenancy_policy.csv | 13 + examples/rbac_with_resource_scope_policy.csv | 8 + rbac_api_test.go | 309 ++++++++++++++++++ 8 files changed, 585 insertions(+) create mode 100644 examples/RBAC_WITH_RESOURCE_SCOPE.md create mode 100644 examples/rbac_with_resource_scope_and_tenant_model.conf create mode 100644 examples/rbac_with_resource_scope_and_tenant_policy.csv create mode 100644 examples/rbac_with_resource_scope_model.conf create mode 100644 examples/rbac_with_resource_scope_multitenancy_model.conf create mode 100644 examples/rbac_with_resource_scope_multitenancy_policy.csv create mode 100644 examples/rbac_with_resource_scope_policy.csv diff --git a/examples/RBAC_WITH_RESOURCE_SCOPE.md b/examples/RBAC_WITH_RESOURCE_SCOPE.md new file mode 100644 index 000000000..a7f9e663c --- /dev/null +++ b/examples/RBAC_WITH_RESOURCE_SCOPE.md @@ -0,0 +1,206 @@ +# RBAC with Resource Scope + +This document explains how to implement Azure RBAC-like resource-scoped roles in Casbin, where the same role can be assigned to different users with different resource scopes. + +## Overview + +Resource-scoped RBAC allows you to assign the same role to different users, but limit the scope of that role to specific resources. This prevents one user's role permissions from affecting another user's permissions, even when they have the same role name. + +## Problem Statement + +In traditional RBAC, if you have: +``` +p, reader, resource1, read +p, reader, resource2, read + +g, user1, reader +g, user2, reader +``` + +Both `user1` and `user2` would have access to both `resource1` and `resource2` because they both have the `reader` role. + +## Solution: Resource-Scoped Roles + +With resource-scoped RBAC, you can scope roles to specific resources: + +### Example 1: Simple Resource Scope + +**Model** (`rbac_with_resource_scope_model.conf`): +```ini +[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, r.obj) && r.obj == p.obj && r.act == p.act +``` + +**Policy** (`rbac_with_resource_scope_policy.csv`): +```csv +p, reader, resource1, read +p, reader, resource2, read + +g, user1, reader, resource1 +g, user2, reader, resource2 +``` + +In this model: +- `user1` has the `reader` role scoped to `resource1` +- `user2` has the `reader` role scoped to `resource2` +- `user1` cannot access `resource2` and vice versa + +### Example 2: Multi-Tenant with Resource Scope + +For multi-tenant scenarios where you need to scope by both tenant and resource: + +**Model** (`rbac_with_resource_scope_multitenancy_model.conf`): +```ini +[request_definition] +r = sub, tenant, obj, act + +[policy_definition] +p = sub, tenant, obj, act + +[role_definition] +g = _, _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub, r.tenant + "::" + r.obj) && r.tenant == p.tenant && r.obj == p.obj && r.act == p.act +``` + +**Policy** (`rbac_with_resource_scope_multitenancy_policy.csv`): +```csv +p, reader, tenant1, resource1, read +p, reader, tenant1, resource2, read +p, reader, tenant2, resource1, read + +g, user1, reader, tenant1::resource1 +g, user2, reader, tenant1::resource2 +g, user3, reader, tenant2::resource1 +``` + +In this model: +- `user1` has the `reader` role for `resource1` in `tenant1` +- `user2` has the `reader` role for `resource2` in `tenant1` +- `user3` has the `reader` role for `resource1` in `tenant2` +- Each user's permissions are isolated to their specific tenant and resource combination + +## Usage + +### Go Code Example + +```go +package main + +import ( + "fmt" + "github.com/casbin/casbin/v2" +) + +func main() { + // Load the model and policy + e, _ := casbin.NewEnforcer( + "examples/rbac_with_resource_scope_model.conf", + "examples/rbac_with_resource_scope_policy.csv", + ) + + // Check permissions + ok, _ := e.Enforce("user1", "resource1", "read") + fmt.Println(ok) // true + + ok, _ = e.Enforce("user1", "resource2", "read") + fmt.Println(ok) // false + + ok, _ = e.Enforce("user2", "resource2", "read") + fmt.Println(ok) // true + + // Get roles for a user with resource scope + roles, _ := e.GetRolesForUser("user1", "resource1") + fmt.Println(roles) // [reader] + + roles, _ = e.GetRolesForUser("user1", "resource2") + fmt.Println(roles) // [] + + // Add a new role assignment with resource scope + e.AddRoleForUser("user3", "writer", "resource1") + + ok, _ = e.Enforce("user3", "resource1", "write") + fmt.Println(ok) // true +} +``` + +### Multi-Tenant Example + +```go +package main + +import ( + "fmt" + "github.com/casbin/casbin/v2" +) + +func main() { + // Load the multi-tenant model and policy + e, _ := casbin.NewEnforcer( + "examples/rbac_with_resource_scope_multitenancy_model.conf", + "examples/rbac_with_resource_scope_multitenancy_policy.csv", + ) + + // Check permissions + ok, _ := e.Enforce("user1", "tenant1", "resource1", "read") + fmt.Println(ok) // true + + ok, _ = e.Enforce("user1", "tenant1", "resource2", "read") + fmt.Println(ok) // false - user1 doesn't have access to resource2 + + ok, _ = e.Enforce("user1", "tenant2", "resource1", "read") + fmt.Println(ok) // false - user1 doesn't have access to tenant2 + + // Get roles for a user with tenant and resource scope + roles, _ := e.GetRolesForUser("user1", "tenant1::resource1") + fmt.Println(roles) // [reader] + + // Add a new role assignment with tenant and resource scope + e.AddRoleForUser("user4", "writer", "tenant1::resource1") +} +``` + +## Key Points + +1. **Third Parameter in Grouping**: The third parameter in the `g` definition specifies the scope (resource or tenant::resource combination). + +2. **Matcher Logic**: The matcher checks that the role assignment matches the requested resource scope using `g(r.sub, p.sub, r.obj)` or `g(r.sub, p.sub, r.tenant + "::" + r.obj)`. + +3. **Isolation**: Users with the same role name but different scopes are completely isolated from each other. + +4. **API Compatibility**: The standard Casbin RBAC APIs work with resource-scoped roles by passing the scope as the domain parameter: + - `GetRolesForUser(user, scope)` + - `GetUsersForRole(role, scope)` + - `AddRoleForUser(user, role, scope)` + +## Comparison with Azure RBAC + +This implementation provides similar functionality to Azure RBAC: + +| Azure RBAC | Casbin Resource-Scoped RBAC | +|------------|----------------------------| +| User assigned "Reader" role for Resource1 | `g, user1, reader, resource1` | +| User assigned "Reader" role for Resource2 | `g, user2, reader, resource2` | +| Roles are reusable across resources | Same role name with different scopes | +| Permissions isolated per assignment | Isolated through scope in grouping | + +## See Also + +- [rbac_with_domains_model.conf](rbac_with_domains_model.conf) - Traditional RBAC with domain support +- [rbac_with_resource_roles_model.conf](rbac_with_resource_roles_model.conf) - RBAC with resource hierarchies diff --git a/examples/rbac_with_resource_scope_and_tenant_model.conf b/examples/rbac_with_resource_scope_and_tenant_model.conf new file mode 100644 index 000000000..d04a469e8 --- /dev/null +++ b/examples/rbac_with_resource_scope_and_tenant_model.conf @@ -0,0 +1,14 @@ +[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, r.obj) && r.obj == p.obj && r.act == p.act diff --git a/examples/rbac_with_resource_scope_and_tenant_policy.csv b/examples/rbac_with_resource_scope_and_tenant_policy.csv new file mode 100644 index 000000000..d8fcb0656 --- /dev/null +++ b/examples/rbac_with_resource_scope_and_tenant_policy.csv @@ -0,0 +1,7 @@ +p, reader, resource1, read +p, reader, resource2, read +p, writer, resource1, write +p, writer, resource2, write + +g, user1, reader, resource1 +g, user2, reader, resource2 diff --git a/examples/rbac_with_resource_scope_model.conf b/examples/rbac_with_resource_scope_model.conf new file mode 100644 index 000000000..d04a469e8 --- /dev/null +++ b/examples/rbac_with_resource_scope_model.conf @@ -0,0 +1,14 @@ +[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, r.obj) && r.obj == p.obj && r.act == p.act diff --git a/examples/rbac_with_resource_scope_multitenancy_model.conf b/examples/rbac_with_resource_scope_multitenancy_model.conf new file mode 100644 index 000000000..a6f4f4cd1 --- /dev/null +++ b/examples/rbac_with_resource_scope_multitenancy_model.conf @@ -0,0 +1,14 @@ +[request_definition] +r = sub, tenant, obj, act + +[policy_definition] +p = sub, tenant, obj, act + +[role_definition] +g = _, _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub, r.tenant + "::" + r.obj) && r.tenant == p.tenant && r.obj == p.obj && r.act == p.act diff --git a/examples/rbac_with_resource_scope_multitenancy_policy.csv b/examples/rbac_with_resource_scope_multitenancy_policy.csv new file mode 100644 index 000000000..983bfc035 --- /dev/null +++ b/examples/rbac_with_resource_scope_multitenancy_policy.csv @@ -0,0 +1,13 @@ +p, reader, tenant1, resource1, read +p, reader, tenant1, resource2, read +p, reader, tenant2, resource1, read +p, reader, tenant2, resource2, read +p, writer, tenant1, resource1, write +p, writer, tenant1, resource2, write +p, writer, tenant2, resource1, write +p, writer, tenant2, resource2, write + +g, user1, reader, tenant1::resource1 +g, user2, reader, tenant1::resource2 +g, user3, reader, tenant2::resource1 +g, user4, writer, tenant1::resource1 diff --git a/examples/rbac_with_resource_scope_policy.csv b/examples/rbac_with_resource_scope_policy.csv new file mode 100644 index 000000000..bbc8a4d6d --- /dev/null +++ b/examples/rbac_with_resource_scope_policy.csv @@ -0,0 +1,8 @@ +p, reader, resource1, read +p, reader, resource2, read +p, writer, resource1, write +p, writer, resource2, write + +g, user1, reader, resource1 +g, user2, reader, resource2 +g, user3, writer, resource1 diff --git a/rbac_api_test.go b/rbac_api_test.go index 7b5948746..a2d402732 100644 --- a/rbac_api_test.go +++ b/rbac_api_test.go @@ -881,3 +881,312 @@ func TestGetImplicitObjectPatternsForUser(t *testing.T) { // Test case 5: non-existent action testGetImplicitObjectPatternsForUser(t, e, "admin", "domain1", "non_existent", []string{}) } + +func TestRBACWithResourceScope(t *testing.T) { + e, err := NewEnforcer("examples/rbac_with_resource_scope_model.conf", "examples/rbac_with_resource_scope_policy.csv") + if err != nil { + t.Fatalf("Failed to create enforcer: %v", err) + } + + // Test 1: user1 should have reader role for resource1 only + has, err := e.Enforce("user1", "resource1", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if !has { + t.Error("user1 should have read access to resource1") + } + + // Test 2: user1 should NOT have access to resource2 (different resource scope) + has, err = e.Enforce("user1", "resource2", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if has { + t.Error("user1 should NOT have read access to resource2") + } + + // Test 3: user2 should have reader role for resource2 only + has, err = e.Enforce("user2", "resource2", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if !has { + t.Error("user2 should have read access to resource2") + } + + // Test 4: user2 should NOT have access to resource1 (different resource scope) + has, err = e.Enforce("user2", "resource1", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if has { + t.Error("user2 should NOT have read access to resource1") + } + + // Test 5: user3 should have writer role for resource1 + has, err = e.Enforce("user3", "resource1", "write") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if !has { + t.Error("user3 should have write access to resource1") + } + + // Test 6: user3 should NOT have write access to resource2 + has, err = e.Enforce("user3", "resource2", "write") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if has { + t.Error("user3 should NOT have write access to resource2") + } + + // Test 7: Verify role assignments with resource scope + roles, err := e.GetRolesForUser("user1", "resource1") + if err != nil { + t.Fatalf("GetRolesForUser failed: %v", err) + } + if len(roles) != 1 || roles[0] != "reader" { + t.Errorf("user1 should have reader role for resource1, got: %v", roles) + } + + // Test 8: Verify user1 has no roles for resource2 + roles, err = e.GetRolesForUser("user1", "resource2") + if err != nil { + t.Fatalf("GetRolesForUser failed: %v", err) + } + if len(roles) != 0 { + t.Errorf("user1 should have no roles for resource2, got: %v", roles) + } + + // Test 9: Get users for reader role in resource1 scope + users, err := e.GetUsersForRole("reader", "resource1") + if err != nil { + t.Fatalf("GetUsersForRole failed: %v", err) + } + if len(users) != 1 || users[0] != "user1" { + t.Errorf("reader role for resource1 should have user1, got: %v", users) + } + + // Test 10: Get users for reader role in resource2 scope + users, err = e.GetUsersForRole("reader", "resource2") + if err != nil { + t.Fatalf("GetUsersForRole failed: %v", err) + } + if len(users) != 1 || users[0] != "user2" { + t.Errorf("reader role for resource2 should have user2, got: %v", users) + } +} + +func TestRBACWithResourceScopeAndTenant(t *testing.T) { + e, err := NewEnforcer("examples/rbac_with_resource_scope_and_tenant_model.conf", "examples/rbac_with_resource_scope_and_tenant_policy.csv") + if err != nil { + t.Fatalf("Failed to create enforcer: %v", err) + } + + // Test 1: user1 should have reader role for resource1 + has, err := e.Enforce("user1", "resource1", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if !has { + t.Error("user1 should have read access to resource1") + } + + // Test 2: user1 should NOT have access to resource2 (different resource scope) + has, err = e.Enforce("user1", "resource2", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if has { + t.Error("user1 should NOT have read access to resource2") + } + + // Test 3: user2 should have reader role for resource2 + has, err = e.Enforce("user2", "resource2", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if !has { + t.Error("user2 should have read access to resource2") + } + + // Test 4: user2 should NOT have access to resource1 (different resource scope) + has, err = e.Enforce("user2", "resource1", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if has { + t.Error("user2 should NOT have read access to resource1") + } + + // Test 5: Verify role assignments with resource scope + roles, err := e.GetRolesForUser("user1", "resource1") + if err != nil { + t.Fatalf("GetRolesForUser failed: %v", err) + } + if len(roles) != 1 || roles[0] != "reader" { + t.Errorf("user1 should have reader role for resource1, got: %v", roles) + } + + // Test 6: Verify user1 has no roles for resource2 + roles, err = e.GetRolesForUser("user1", "resource2") + if err != nil { + t.Fatalf("GetRolesForUser failed: %v", err) + } + if len(roles) != 0 { + t.Errorf("user1 should have no roles for resource2, got: %v", roles) + } + + // Test 7: Get users for reader role in resource1 scope + users, err := e.GetUsersForRole("reader", "resource1") + if err != nil { + t.Fatalf("GetUsersForRole failed: %v", err) + } + if len(users) != 1 || users[0] != "user1" { + t.Errorf("reader role for resource1 should have user1, got: %v", users) + } + + // Test 8: Get users for reader role in resource2 scope + users, err = e.GetUsersForRole("reader", "resource2") + if err != nil { + t.Fatalf("GetUsersForRole failed: %v", err) + } + if len(users) != 1 || users[0] != "user2" { + t.Errorf("reader role for resource2 should have user2, got: %v", users) + } + + // Test 9: Verify adding a new role assignment with resource scope + added, err := e.AddRoleForUser("user3", "writer", "resource1") + if err != nil { + t.Fatalf("AddRoleForUser failed: %v", err) + } + if !added { + t.Error("Failed to add writer role for user3") + } + + // Test 10: Verify user3 can now write to resource1 + has, err = e.Enforce("user3", "resource1", "write") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if !has { + t.Error("user3 should have write access to resource1 after role assignment") + } +} + +func TestRBACWithResourceScopeMultitenancy(t *testing.T) { + e, err := NewEnforcer("examples/rbac_with_resource_scope_multitenancy_model.conf", "examples/rbac_with_resource_scope_multitenancy_policy.csv") + if err != nil { + t.Fatalf("Failed to create enforcer: %v", err) + } + + // Test 1: user1 has reader role for resource1 in tenant1 + has, err := e.Enforce("user1", "tenant1", "resource1", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if !has { + t.Error("user1 should have read access to resource1 in tenant1") + } + + // Test 2: user1 should NOT have access to resource2 in tenant1 (different resource) + has, err = e.Enforce("user1", "tenant1", "resource2", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if has { + t.Error("user1 should NOT have read access to resource2 in tenant1") + } + + // Test 3: user1 should NOT have access to resource1 in tenant2 (different tenant) + has, err = e.Enforce("user1", "tenant2", "resource1", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if has { + t.Error("user1 should NOT have read access to resource1 in tenant2") + } + + // Test 4: user2 has reader role for resource2 in tenant1 + has, err = e.Enforce("user2", "tenant1", "resource2", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if !has { + t.Error("user2 should have read access to resource2 in tenant1") + } + + // Test 5: user3 has reader role for resource1 in tenant2 + has, err = e.Enforce("user3", "tenant2", "resource1", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if !has { + t.Error("user3 should have read access to resource1 in tenant2") + } + + // Test 6: user3 should NOT have access to resource1 in tenant1 (different tenant) + has, err = e.Enforce("user3", "tenant1", "resource1", "read") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if has { + t.Error("user3 should NOT have read access to resource1 in tenant1") + } + + // Test 7: user4 has writer role for resource1 in tenant1 + has, err = e.Enforce("user4", "tenant1", "resource1", "write") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if !has { + t.Error("user4 should have write access to resource1 in tenant1") + } + + // Test 8: user4 should NOT have write access to resource2 in tenant1 (different resource) + has, err = e.Enforce("user4", "tenant1", "resource2", "write") + if err != nil { + t.Fatalf("Enforce failed: %v", err) + } + if has { + t.Error("user4 should NOT have write access to resource2 in tenant1") + } + + // Test 9: Verify role assignments - user1 should have reader role for resource1 in tenant1 + roles, err := e.GetRolesForUser("user1", "tenant1::resource1") + if err != nil { + t.Fatalf("GetRolesForUser failed: %v", err) + } + if len(roles) != 1 || roles[0] != "reader" { + t.Errorf("user1 should have reader role for tenant1::resource1, got: %v", roles) + } + + // Test 10: Verify user1 has no roles for resource1 in tenant2 + roles, err = e.GetRolesForUser("user1", "tenant2::resource1") + if err != nil { + t.Fatalf("GetRolesForUser failed: %v", err) + } + if len(roles) != 0 { + t.Errorf("user1 should have no roles for tenant2::resource1, got: %v", roles) + } + + // Test 11: Get users for reader role in tenant1 resource1 scope + users, err := e.GetUsersForRole("reader", "tenant1::resource1") + if err != nil { + t.Fatalf("GetUsersForRole failed: %v", err) + } + if len(users) != 1 || users[0] != "user1" { + t.Errorf("reader role for tenant1::resource1 should have user1, got: %v", users) + } + + // Test 12: Get users for reader role in tenant2 resource1 scope + users, err = e.GetUsersForRole("reader", "tenant2::resource1") + if err != nil { + t.Fatalf("GetUsersForRole failed: %v", err) + } + if len(users) != 1 || users[0] != "user3" { + t.Errorf("reader role for tenant2::resource1 should have user3, got: %v", users) + } +} From 2dcd484e1f4ed6562a784d85970fa494ac846bdd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:08:27 +0000 Subject: [PATCH 3/4] Add demo program for resource-scoped RBAC Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- examples/rbac_with_resource_scope_demo.go | 101 ++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 examples/rbac_with_resource_scope_demo.go diff --git a/examples/rbac_with_resource_scope_demo.go b/examples/rbac_with_resource_scope_demo.go new file mode 100644 index 000000000..12e255a11 --- /dev/null +++ b/examples/rbac_with_resource_scope_demo.go @@ -0,0 +1,101 @@ +// Copyright 2025 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "log" + + "github.com/casbin/casbin/v2" +) + +func main() { + fmt.Println("=== RBAC with Resource Scope Demo ===\n") + + // Example 1: Simple Resource Scope + fmt.Println("Example 1: Simple Resource Scope") + fmt.Println("----------------------------------") + + e1, err := casbin.NewEnforcer("rbac_with_resource_scope_model.conf", "rbac_with_resource_scope_policy.csv") + if err != nil { + log.Fatal(err) + } + + // user1 has reader role scoped to resource1 + ok, _ := e1.Enforce("user1", "resource1", "read") + fmt.Printf("user1 can read resource1: %v\n", ok) // true + + ok, _ = e1.Enforce("user1", "resource2", "read") + fmt.Printf("user1 can read resource2: %v\n", ok) // false + + // user2 has reader role scoped to resource2 + ok, _ = e1.Enforce("user2", "resource2", "read") + fmt.Printf("user2 can read resource2: %v\n", ok) // true + + ok, _ = e1.Enforce("user2", "resource1", "read") + fmt.Printf("user2 can read resource1: %v\n", ok) // false + + roles, _ := e1.GetRolesForUser("user1", "resource1") + fmt.Printf("user1's roles for resource1: %v\n", roles) + + fmt.Println() + + // Example 2: Multi-Tenant with Resource Scope + fmt.Println("Example 2: Multi-Tenant with Resource Scope") + fmt.Println("--------------------------------------------") + + e2, err := casbin.NewEnforcer("rbac_with_resource_scope_multitenancy_model.conf", "rbac_with_resource_scope_multitenancy_policy.csv") + if err != nil { + log.Fatal(err) + } + + // user1 has reader role for resource1 in tenant1 + ok, _ = e2.Enforce("user1", "tenant1", "resource1", "read") + fmt.Printf("user1 can read resource1 in tenant1: %v\n", ok) // true + + ok, _ = e2.Enforce("user1", "tenant1", "resource2", "read") + fmt.Printf("user1 can read resource2 in tenant1: %v\n", ok) // false + + ok, _ = e2.Enforce("user1", "tenant2", "resource1", "read") + fmt.Printf("user1 can read resource1 in tenant2: %v\n", ok) // false + + // user3 has reader role for resource1 in tenant2 + ok, _ = e2.Enforce("user3", "tenant2", "resource1", "read") + fmt.Printf("user3 can read resource1 in tenant2: %v\n", ok) // true + + roles, _ = e2.GetRolesForUser("user1", "tenant1::resource1") + fmt.Printf("user1's roles for resource1 in tenant1: %v\n", roles) + + roles, _ = e2.GetRolesForUser("user3", "tenant2::resource1") + fmt.Printf("user3's roles for resource1 in tenant2: %v\n", roles) + + fmt.Println() + + // Example 3: Dynamic Role Assignment + fmt.Println("Example 3: Dynamic Role Assignment") + fmt.Println("-----------------------------------") + + // Add a new role assignment with resource scope + added, _ := e1.AddRoleForUser("user4", "writer", "resource1") + fmt.Printf("Added writer role for user4 on resource1: %v\n", added) + + ok, _ = e1.Enforce("user4", "resource1", "write") + fmt.Printf("user4 can write to resource1: %v\n", ok) // true + + ok, _ = e1.Enforce("user4", "resource2", "write") + fmt.Printf("user4 can write to resource2: %v\n", ok) // false + + fmt.Println("\n=== Demo Complete ===") +} From a818a1800c94fef2deebfa11fd7c6626632a43ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:11:40 +0000 Subject: [PATCH 4/4] Fix linting issue in demo program Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- examples/rbac_with_resource_scope_demo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rbac_with_resource_scope_demo.go b/examples/rbac_with_resource_scope_demo.go index 12e255a11..49c485973 100644 --- a/examples/rbac_with_resource_scope_demo.go +++ b/examples/rbac_with_resource_scope_demo.go @@ -22,7 +22,7 @@ import ( ) func main() { - fmt.Println("=== RBAC with Resource Scope Demo ===\n") + fmt.Println("=== RBAC with Resource Scope Demo ===") // Example 1: Simple Resource Scope fmt.Println("Example 1: Simple Resource Scope")