this repo has no description
1package rbac
2
3import (
4 "database/sql"
5 "path"
6 "strings"
7
8 sqladapter "github.com/Blank-Xu/sql-adapter"
9 "github.com/casbin/casbin/v2"
10 "github.com/casbin/casbin/v2/model"
11)
12
13const (
14 Model = `
15[request_definition]
16r = sub, dom, obj, act
17
18[policy_definition]
19p = sub, dom, obj, act
20
21[role_definition]
22g = _, _, _
23
24[policy_effect]
25e = some(where (p.eft == allow))
26
27[matchers]
28m = r.act == p.act && r.dom == p.dom && keyMatch2(r.obj, p.obj) && g(r.sub, p.sub, r.dom)
29`
30)
31
32type Enforcer struct {
33 E *casbin.SyncedEnforcer
34}
35
36func keyMatch2(key1 string, key2 string) bool {
37 matched, _ := path.Match(key2, key1)
38 return matched
39}
40
41func NewEnforcer(path string) (*Enforcer, error) {
42 m, err := model.NewModelFromString(Model)
43 if err != nil {
44 return nil, err
45 }
46
47 db, err := sql.Open("sqlite3", path)
48 if err != nil {
49 return nil, err
50 }
51
52 a, err := sqladapter.NewAdapter(db, "sqlite3", "acl")
53 if err != nil {
54 return nil, err
55 }
56
57 e, err := casbin.NewSyncedEnforcer(m, a)
58 if err != nil {
59 return nil, err
60 }
61
62 e.EnableAutoSave(true)
63 e.AddFunction("keyMatch2", keyMatch2Func)
64
65 return &Enforcer{e}, nil
66}
67
68func (e *Enforcer) AddDomain(domain string) error {
69 // Add policies with patterns
70 _, err := e.E.AddPolicies([][]string{
71 {"server:owner", domain, domain, "server:invite"},
72 {"server:member", domain, domain, "repo:create"},
73 })
74 if err != nil {
75 return err
76 }
77
78 // all owners are also members
79 _, err = e.E.AddGroupingPolicy("server:owner", "server:member", domain)
80 return err
81}
82
83func (e *Enforcer) GetDomainsForUser(did string) ([]string, error) {
84 return e.E.Enforcer.GetDomainsForUser(did)
85}
86
87func (e *Enforcer) AddOwner(domain, owner string) error {
88 _, err := e.E.AddGroupingPolicy(owner, "server:owner", domain)
89 return err
90}
91
92func (e *Enforcer) AddMember(domain, member string) error {
93 _, err := e.E.AddGroupingPolicy(member, "server:member", domain)
94 return err
95}
96
97func (e *Enforcer) AddRepo(member, domain, repo string) error {
98 _, err := e.E.AddPolicies([][]string{
99 {member, domain, repo, "repo:push"},
100 {member, domain, repo, "repo:owner"},
101 {member, domain, repo, "repo:invite"},
102 {member, domain, repo, "repo:delete"},
103 {"server:owner", domain, repo, "repo:delete"}, // server owner can delete any repo
104 })
105 return err
106}
107
108func (e *Enforcer) GetUserByRole(role, domain string) ([]string, error) {
109 var membersWithoutRoles []string
110
111 // this includes roles too, casbin does not differentiate.
112 // the filtering criteria is to remove strings not starting with `did:`
113 members, err := e.E.Enforcer.GetImplicitUsersForRole(role, domain)
114 for _, m := range members {
115 if strings.HasPrefix(m, "did:") {
116 membersWithoutRoles = append(membersWithoutRoles, m)
117 }
118 }
119 if err != nil {
120 return nil, err
121 }
122
123 return membersWithoutRoles, nil
124}
125
126func (e *Enforcer) isRole(user, role, domain string) (bool, error) {
127 return e.E.HasGroupingPolicy(user, role, domain)
128}
129
130func (e *Enforcer) IsServerOwner(user, domain string) (bool, error) {
131 return e.isRole(user, "server:owner", domain)
132}
133
134func (e *Enforcer) IsServerMember(user, domain string) (bool, error) {
135 return e.isRole(user, "server:member", domain)
136}
137
138func (e *Enforcer) IsPushAllowed(user, domain, repo string) (bool, error) {
139 return e.E.Enforce(user, domain, repo, "repo:push")
140}
141
142// keyMatch2Func is a wrapper for keyMatch2 to make it compatible with Casbin
143func keyMatch2Func(args ...interface{}) (interface{}, error) {
144 name1 := args[0].(string)
145 name2 := args[1].(string)
146
147 return keyMatch2(name1, name2), nil
148}