forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
this repo has no description
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package rbac
2
3import (
4 "database/sql"
5 "fmt"
6 "path"
7 "strings"
8
9 adapter "github.com/Blank-Xu/sql-adapter"
10 "github.com/casbin/casbin/v2"
11 "github.com/casbin/casbin/v2/model"
12)
13
14const (
15 Model = `
16[request_definition]
17r = sub, dom, obj, act
18
19[policy_definition]
20p = sub, dom, obj, act
21
22[role_definition]
23g = _, _, _
24
25[policy_effect]
26e = some(where (p.eft == allow))
27
28[matchers]
29m = r.act == p.act && r.dom == p.dom && keyMatch2(r.obj, p.obj) && g(r.sub, p.sub, r.dom)
30`
31)
32
33type Enforcer struct {
34 E *casbin.Enforcer
35}
36
37func keyMatch2(key1 string, key2 string) bool {
38 matched, _ := path.Match(key2, key1)
39 return matched
40}
41
42func NewEnforcer(path string) (*Enforcer, error) {
43 m, err := model.NewModelFromString(Model)
44 if err != nil {
45 return nil, err
46 }
47
48 db, err := sql.Open("sqlite3", path)
49 if err != nil {
50 return nil, err
51 }
52
53 a, err := adapter.NewAdapter(db, "sqlite3", "acl")
54 if err != nil {
55 return nil, err
56 }
57
58 e, err := casbin.NewEnforcer(m, a)
59 if err != nil {
60 return nil, err
61 }
62
63 e.EnableAutoSave(false)
64
65 e.AddFunction("keyMatch2", keyMatch2Func)
66
67 return &Enforcer{e}, nil
68}
69
70func (e *Enforcer) AddDomain(domain string) error {
71 // Add policies with patterns
72 _, err := e.E.AddPolicies([][]string{
73 {"server:owner", domain, domain, "server:invite"},
74 {"server:member", domain, domain, "repo:create"},
75 })
76 if err != nil {
77 return err
78 }
79
80 // all owners are also members
81 _, err = e.E.AddGroupingPolicy("server:owner", "server:member", domain)
82 return err
83}
84
85func (e *Enforcer) GetDomainsForUser(did string) ([]string, error) {
86 return e.E.GetDomainsForUser(did)
87}
88
89func (e *Enforcer) AddOwner(domain, owner string) error {
90 _, err := e.E.AddGroupingPolicy(owner, "server:owner", domain)
91 return err
92}
93
94func (e *Enforcer) AddMember(domain, member string) error {
95 _, err := e.E.AddGroupingPolicy(member, "server:member", domain)
96 return err
97}
98
99func repoPolicies(member, domain, repo string) [][]string {
100 return [][]string{
101 {member, domain, repo, "repo:settings"},
102 {member, domain, repo, "repo:push"},
103 {member, domain, repo, "repo:owner"},
104 {member, domain, repo, "repo:invite"},
105 {member, domain, repo, "repo:delete"},
106 {"server:owner", domain, repo, "repo:delete"}, // server owner can delete any repo
107 }
108}
109func (e *Enforcer) AddRepo(member, domain, repo string) error {
110 err := checkRepoFormat(repo)
111 if err != nil {
112 return err
113 }
114
115 _, err = e.E.AddPolicies(repoPolicies(member, domain, repo))
116 return err
117}
118func (e *Enforcer) RemoveRepo(member, domain, repo string) error {
119 err := checkRepoFormat(repo)
120 if err != nil {
121 return err
122 }
123
124 _, err = e.E.RemovePolicies(repoPolicies(member, domain, repo))
125 return err
126}
127
128var (
129 collaboratorPolicies = func(collaborator, domain, repo string) [][]string {
130 return [][]string{
131 {collaborator, domain, repo, "repo:collaborator"},
132 {collaborator, domain, repo, "repo:settings"},
133 {collaborator, domain, repo, "repo:push"},
134 }
135 }
136)
137
138func (e *Enforcer) AddCollaborator(collaborator, domain, repo string) error {
139 err := checkRepoFormat(repo)
140 if err != nil {
141 return err
142 }
143
144 _, err = e.E.AddPolicies(collaboratorPolicies(collaborator, domain, repo))
145 return err
146}
147
148func (e *Enforcer) RemoveCollaborator(collaborator, domain, repo string) error {
149 err := checkRepoFormat(repo)
150 if err != nil {
151 return err
152 }
153
154 _, err = e.E.RemovePolicies(collaboratorPolicies(collaborator, domain, repo))
155 return err
156}
157
158func (e *Enforcer) GetUserByRole(role, domain string) ([]string, error) {
159 var membersWithoutRoles []string
160
161 // this includes roles too, casbin does not differentiate.
162 // the filtering criteria is to remove strings not starting with `did:`
163 members, err := e.E.GetImplicitUsersForRole(role, domain)
164 for _, m := range members {
165 if strings.HasPrefix(m, "did:") {
166 membersWithoutRoles = append(membersWithoutRoles, m)
167 }
168 }
169 if err != nil {
170 return nil, err
171 }
172
173 return membersWithoutRoles, nil
174}
175
176func (e *Enforcer) isRole(user, role, domain string) (bool, error) {
177 return e.E.HasGroupingPolicy(user, role, domain)
178}
179
180func (e *Enforcer) IsServerOwner(user, domain string) (bool, error) {
181 return e.isRole(user, "server:owner", domain)
182}
183
184func (e *Enforcer) IsServerMember(user, domain string) (bool, error) {
185 return e.isRole(user, "server:member", domain)
186}
187
188func (e *Enforcer) IsPushAllowed(user, domain, repo string) (bool, error) {
189 return e.E.Enforce(user, domain, repo, "repo:push")
190}
191
192func (e *Enforcer) IsSettingsAllowed(user, domain, repo string) (bool, error) {
193 return e.E.Enforce(user, domain, repo, "repo:settings")
194}
195
196func (e *Enforcer) IsCollaboratorInviteAllowed(user, domain, repo string) (bool, error) {
197 return e.E.Enforce(user, domain, repo, "repo:invite")
198}
199
200// given a repo, what permissions does this user have? repo:owner? repo:invite? etc.
201func (e *Enforcer) GetPermissionsInRepo(user, domain, repo string) []string {
202 var permissions []string
203 res := e.E.GetPermissionsForUserInDomain(user, domain)
204 for _, p := range res {
205 // get only permissions for this resource/repo
206 if p[2] == repo {
207 permissions = append(permissions, p[3])
208 }
209 }
210
211 return permissions
212}
213
214// keyMatch2Func is a wrapper for keyMatch2 to make it compatible with Casbin
215func keyMatch2Func(args ...interface{}) (interface{}, error) {
216 name1 := args[0].(string)
217 name2 := args[1].(string)
218
219 return keyMatch2(name1, name2), nil
220}
221
222func checkRepoFormat(repo string) error {
223 // sanity check, repo must be of the form ownerDid/repo
224 if parts := strings.SplitN(repo, "/", 2); !strings.HasPrefix(parts[0], "did:") {
225 return fmt.Errorf("invalid repo: %s", repo)
226 }
227
228 return nil
229}