Skip to content

Commit cd99ceb

Browse files
Feature: Define RoleManager as interface
Define RoleManager as interface, and rename existing RoleManager struct to defaultRoleManager to provide a default implementation and backwards compatability. Defines RoleManagerConstructor to allow specifying a function for creating a new RoleManager. Adds SetRoleManagerConstructor method to Model to allow setting the function to call for a new instance of RoleManager. Adds implementation of RoleManagerConstructor to Model that will create an instance of the defaultRoleManager just like before. Updates Assertion to reference RoleManager interface instead of an instance. Updates Assertions.buildRoleLinks() to use newRoleManagerFunc that holds the RoleManagerConstructor. All test are passing with the only change being *RoleManager to RoleManager.
1 parent 748b815 commit cd99ceb

File tree

8 files changed

+289
-216
lines changed

8 files changed

+289
-216
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,24 @@ RESTful | [keymatch_model.conf](https://github.com/casbin/casbin/blob/master/exa
175175
Deny-override | [rbac_model_with_deny.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_model_with_deny.conf) | [rbac_policy_with_deny.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_policy_with_deny.csv)
176176
Priority | [priority_model.conf](https://github.com/casbin/casbin/blob/master/examples/priority_model.conf) | [priority_policy.csv](https://github.com/casbin/casbin/blob/master/examples/priority_policy.csv)
177177
178+
## RoleManager
179+
180+
To use a custom RoleManager implementation.
181+
182+
```go
183+
184+
type myCustomRoleManager struct {} // assumes the type satisfies the RoleManager interface
185+
186+
func newRoleManager() rbac.RoleManagerConstructor {
187+
return func() rbac.RoleManager {
188+
return &myCustomRoleManager{}
189+
}
190+
}
191+
192+
e := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv")
193+
e.SetRoleManager(newRoleManager())
194+
```
195+
178196
## How to use Casbin as a service?
179197
180198
- [Go-Simple-API-Gateway](https://github.com/Soontao/go-simple-api-gateway): A simple API gateway written by golang, supports for authentication and authorization

enforcer.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/casbin/casbin/file-adapter"
2323
"github.com/casbin/casbin/model"
2424
"github.com/casbin/casbin/persist"
25+
"github.com/casbin/casbin/rbac"
2526
"github.com/casbin/casbin/util"
2627
)
2728

@@ -40,6 +41,7 @@ type Enforcer struct {
4041
modelPath string
4142
model model.Model
4243
fm model.FunctionMap
44+
rmc rbac.RoleManagerConstructor
4345

4446
adapter persist.Adapter
4547

@@ -55,6 +57,7 @@ type Enforcer struct {
5557
// e := casbin.NewEnforcer("path/to/basic_model.conf", a)
5658
func NewEnforcer(params ...interface{}) *Enforcer {
5759
e := &Enforcer{}
60+
e.rmc = rbac.DefaultRoleManager()
5861

5962
parsedParamLen := 0
6063
if len(params) >= 1 && reflect.TypeOf(params[len(params)-1]).Kind() == reflect.Bool {
@@ -182,6 +185,11 @@ func (e *Enforcer) SetAdapter(adapter persist.Adapter) {
182185
e.adapter = adapter
183186
}
184187

188+
// SetRoleManager sets the constructor function for creating a RoleManager.
189+
func (e *Enforcer) SetRoleManager(rmc rbac.RoleManagerConstructor) {
190+
e.rmc = rmc
191+
}
192+
185193
// ClearPolicy clears all policy.
186194
func (e *Enforcer) ClearPolicy() {
187195
e.model.ClearPolicy()
@@ -196,7 +204,7 @@ func (e *Enforcer) LoadPolicy() error {
196204
}
197205

198206
e.model.PrintPolicy()
199-
e.model.BuildRoleLinks()
207+
e.model.BuildRoleLinks(e.rmc)
200208
return nil
201209
}
202210

management_api.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func (e *Enforcer) AddGroupingPolicy(params ...interface{}) bool {
144144
ruleAdded = e.addPolicy("g", "g", policy)
145145
}
146146

147-
e.model.BuildRoleLinks()
147+
e.model.BuildRoleLinks(e.rmc)
148148
return ruleAdded
149149
}
150150

@@ -162,14 +162,14 @@ func (e *Enforcer) RemoveGroupingPolicy(params ...interface{}) bool {
162162
ruleRemoved = e.removePolicy("g", "g", policy)
163163
}
164164

165-
e.model.BuildRoleLinks()
165+
e.model.BuildRoleLinks(e.rmc)
166166
return ruleRemoved
167167
}
168168

169169
// RemoveFilteredGroupingPolicy removes a role inheritance rule from the current policy, field filters can be specified.
170170
func (e *Enforcer) RemoveFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) bool {
171171
ruleRemoved := e.removeFilteredPolicy("g", "g", fieldIndex, fieldValues...)
172-
e.model.BuildRoleLinks()
172+
e.model.BuildRoleLinks(e.rmc)
173173
return ruleRemoved
174174
}
175175

model/assertion.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ type Assertion struct {
2626
Value string
2727
Tokens []string
2828
Policy [][]string
29-
RM *rbac.RoleManager
29+
RM rbac.RoleManager
3030
}
3131

32-
func (ast *Assertion) buildRoleLinks() {
33-
ast.RM = rbac.NewRoleManager(10)
32+
func (ast *Assertion) buildRoleLinks(rmc rbac.RoleManagerConstructor) {
33+
ast.RM = rmc()
3434
for _, rule := range ast.Policy {
3535
if len(rule) == 2 {
3636
ast.RM.AddLink(rule[0], rule[1])

model/policy.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
package model
1616

1717
import (
18+
"github.com/casbin/casbin/rbac"
1819
"github.com/casbin/casbin/util"
1920
)
2021

2122
// BuildRoleLinks initializes the roles in RBAC.
22-
func (model Model) BuildRoleLinks() {
23+
func (model Model) BuildRoleLinks(rmc rbac.RoleManagerConstructor) {
2324
for _, ast := range model["g"] {
24-
ast.buildRoleLinks()
25+
ast.buildRoleLinks(rmc)
2526
}
2627
}
2728

rbac/default_role_manager.go

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// Copyright 2017 The casbin Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package rbac
16+
17+
import (
18+
"github.com/casbin/casbin/util"
19+
)
20+
21+
type defaultRoleManager struct {
22+
allRoles map[string]*Role
23+
level int
24+
}
25+
26+
// DefaultRoleManager provides an implementation for the RoleManagerConstructor
27+
// that creates the default RoleManager as it was previously created.
28+
func DefaultRoleManager() RoleManagerConstructor {
29+
return func() RoleManager {
30+
return NewDefaultRoleManager(10)
31+
}
32+
}
33+
34+
// NewDefaultRoleManager is the constructor for creating an instance of the
35+
// default RoleManager implementation..
36+
func NewDefaultRoleManager(level int) RoleManager {
37+
rm := defaultRoleManager{}
38+
rm.allRoles = make(map[string]*Role)
39+
rm.level = level
40+
return &rm
41+
}
42+
43+
func (rm *defaultRoleManager) hasRole(name string) bool {
44+
_, ok := rm.allRoles[name]
45+
return ok
46+
}
47+
48+
func (rm *defaultRoleManager) createRole(name string) *Role {
49+
if !rm.hasRole(name) {
50+
rm.allRoles[name] = newRole(name)
51+
}
52+
53+
return rm.allRoles[name]
54+
}
55+
56+
// AddLink adds the inheritance link between role: name1 and role: name2.
57+
// aka role: name1 inherits role: name2.
58+
// domain is a prefix to the roles.
59+
func (rm *defaultRoleManager) AddLink(name1 string, name2 string, domain ...string) {
60+
if len(domain) == 1 {
61+
name1 = domain[0] + "::" + name1
62+
name2 = domain[0] + "::" + name2
63+
}
64+
65+
role1 := rm.createRole(name1)
66+
role2 := rm.createRole(name2)
67+
role1.addRole(role2)
68+
}
69+
70+
// DeleteLink deletes the inheritance link between role: name1 and role: name2.
71+
// aka role: name1 does not inherit role: name2 any more.
72+
// domain is a prefix to the roles.
73+
func (rm *defaultRoleManager) DeleteLink(name1 string, name2 string, domain ...string) {
74+
if len(domain) == 1 {
75+
name1 = domain[0] + "::" + name1
76+
name2 = domain[0] + "::" + name2
77+
}
78+
79+
if !rm.hasRole(name1) || !rm.hasRole(name2) {
80+
return
81+
}
82+
83+
role1 := rm.createRole(name1)
84+
role2 := rm.createRole(name2)
85+
role1.deleteRole(role2)
86+
}
87+
88+
// HasLink determines whether role: name1 inherits role: name2.
89+
// domain is a prefix to the roles.
90+
func (rm *defaultRoleManager) HasLink(name1 string, name2 string, domain ...string) bool {
91+
if len(domain) == 1 {
92+
name1 = domain[0] + "::" + name1
93+
name2 = domain[0] + "::" + name2
94+
}
95+
96+
if name1 == name2 {
97+
return true
98+
}
99+
100+
if !rm.hasRole(name1) || !rm.hasRole(name2) {
101+
return false
102+
}
103+
104+
role1 := rm.createRole(name1)
105+
return role1.hasRole(name2, rm.level)
106+
}
107+
108+
// GetRoles gets the roles that a subject inherits.
109+
// domain is a prefix to the roles.
110+
func (rm *defaultRoleManager) GetRoles(name string, domain ...string) []string {
111+
if len(domain) == 1 {
112+
name = domain[0] + "::" + name
113+
}
114+
115+
if !rm.hasRole(name) {
116+
return nil
117+
}
118+
119+
roles := rm.createRole(name).getRoles()
120+
if len(domain) == 1 {
121+
for i := range roles {
122+
roles[i] = roles[i][len(domain[0])+2:]
123+
}
124+
}
125+
return roles
126+
}
127+
128+
// GetUsers gets the users that inherits a subject.
129+
func (rm *defaultRoleManager) GetUsers(name string) []string {
130+
if !rm.hasRole(name) {
131+
return nil
132+
}
133+
134+
names := []string{}
135+
for _, role := range rm.allRoles {
136+
if role.hasDirectRole(name) {
137+
names = append(names, role.name)
138+
}
139+
}
140+
return names
141+
}
142+
143+
// PrintRoles prints all the roles to log.
144+
func (rm *defaultRoleManager) PrintRoles() {
145+
for _, role := range rm.allRoles {
146+
util.LogPrint(role.toString())
147+
}
148+
}
149+
150+
// Role represents the data structure for a role in RBAC.
151+
type Role struct {
152+
name string
153+
roles []*Role
154+
}
155+
156+
func newRole(name string) *Role {
157+
r := Role{}
158+
r.name = name
159+
return &r
160+
}
161+
162+
func (r *Role) addRole(role *Role) {
163+
for _, rr := range r.roles {
164+
if rr.name == role.name {
165+
return
166+
}
167+
}
168+
169+
r.roles = append(r.roles, role)
170+
}
171+
172+
func (r *Role) deleteRole(role *Role) {
173+
for i, rr := range r.roles {
174+
if rr.name == role.name {
175+
r.roles = append(r.roles[:i], r.roles[i+1:]...)
176+
return
177+
}
178+
}
179+
}
180+
181+
func (r *Role) hasRole(name string, level int) bool {
182+
if r.name == name {
183+
return true
184+
}
185+
186+
if level <= 0 {
187+
return false
188+
}
189+
190+
for _, role := range r.roles {
191+
if role.hasRole(name, level-1) {
192+
return true
193+
}
194+
}
195+
return false
196+
}
197+
198+
func (r *Role) hasDirectRole(name string) bool {
199+
for _, role := range r.roles {
200+
if role.name == name {
201+
return true
202+
}
203+
}
204+
205+
return false
206+
}
207+
208+
func (r *Role) toString() string {
209+
names := ""
210+
for i, role := range r.roles {
211+
if i == 0 {
212+
names += role.name
213+
} else {
214+
names += ", " + role.name
215+
}
216+
}
217+
return r.name + " < " + names
218+
}
219+
220+
func (r *Role) getRoles() []string {
221+
names := []string{}
222+
for _, role := range r.roles {
223+
names = append(names, role.name)
224+
}
225+
return names
226+
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"github.com/casbin/casbin/util"
2222
)
2323

24-
func testRole(t *testing.T, rm *RoleManager, name1 string, name2 string, res bool) {
24+
func testRole(t *testing.T, rm RoleManager, name1 string, name2 string, res bool) {
2525
myRes := rm.HasLink(name1, name2)
2626
log.Printf("%s, %s: %t", name1, name2, myRes)
2727

@@ -30,7 +30,7 @@ func testRole(t *testing.T, rm *RoleManager, name1 string, name2 string, res boo
3030
}
3131
}
3232

33-
func testDomainRole(t *testing.T, rm *RoleManager, name1 string, name2 string, domain string, res bool) {
33+
func testDomainRole(t *testing.T, rm RoleManager, name1 string, name2 string, domain string, res bool) {
3434
myRes := rm.HasLink(name1, name2, domain)
3535
log.Printf("%s :: %s, %s: %t", domain, name1, name2, myRes)
3636

@@ -39,7 +39,7 @@ func testDomainRole(t *testing.T, rm *RoleManager, name1 string, name2 string, d
3939
}
4040
}
4141

42-
func testPrintRoles(t *testing.T, rm *RoleManager, name string, res []string) {
42+
func testPrintRoles(t *testing.T, rm RoleManager, name string, res []string) {
4343
myRes := rm.GetRoles(name)
4444
log.Printf("%s: %s", name, myRes)
4545

@@ -49,7 +49,7 @@ func testPrintRoles(t *testing.T, rm *RoleManager, name string, res []string) {
4949
}
5050

5151
func TestRole(t *testing.T) {
52-
rm := NewRoleManager(3)
52+
rm := NewDefaultRoleManager(3)
5353
rm.AddLink("u1", "g1")
5454
rm.AddLink("u2", "g1")
5555
rm.AddLink("u3", "g2")
@@ -118,7 +118,7 @@ func TestRole(t *testing.T) {
118118
}
119119

120120
func TestDomainRole(t *testing.T) {
121-
rm := NewRoleManager(3)
121+
rm := NewDefaultRoleManager(3)
122122
rm.AddLink("u1", "g1", "domain1")
123123
rm.AddLink("u2", "g1", "domain1")
124124
rm.AddLink("u3", "admin", "domain2")

0 commit comments

Comments
 (0)