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 "slices"
6 "strings"
7
8 adapter "github.com/Blank-Xu/sql-adapter"
9 "github.com/casbin/casbin/v2"
10 "github.com/casbin/casbin/v2/model"
11)
12
13const (
14 ThisServer = "thisserver" // resource identifier for local rbac enforcement
15)
16
17const (
18 Model = `
19[request_definition]
20r = sub, dom, obj, act
21
22[policy_definition]
23p = sub, dom, obj, act
24
25[role_definition]
26g = _, _, _
27
28[policy_effect]
29e = some(where (p.eft == allow))
30
31[matchers]
32m = r.act == p.act && r.dom == p.dom && r.obj == p.obj && g(r.sub, p.sub, r.dom)
33`
34)
35
36type Enforcer struct {
37 E *casbin.Enforcer
38}
39
40func NewEnforcer(path string) (*Enforcer, error) {
41 m, err := model.NewModelFromString(Model)
42 if err != nil {
43 return nil, err
44 }
45
46 db, err := sql.Open("sqlite3", path+"?_foreign_keys=1")
47 if err != nil {
48 return nil, err
49 }
50
51 a, err := adapter.NewAdapter(db, "sqlite3", "acl")
52 if err != nil {
53 return nil, err
54 }
55
56 e, err := casbin.NewEnforcer(m, a)
57 if err != nil {
58 return nil, err
59 }
60
61 e.EnableAutoSave(false)
62
63 return &Enforcer{e}, nil
64}
65
66func (e *Enforcer) AddKnot(knot string) error {
67 // Add policies with patterns
68 _, err := e.E.AddPolicies([][]string{
69 {"server:owner", knot, knot, "server:invite"},
70 {"server:member", knot, knot, "repo:create"},
71 })
72 if err != nil {
73 return err
74 }
75
76 // all owners are also members
77 _, err = e.E.AddGroupingPolicy("server:owner", "server:member", knot)
78 return err
79}
80
81func (e *Enforcer) AddSpindle(spindle string) error {
82 // the internal repr for spindles is spindle:foo.com
83 spindle = intoSpindle(spindle)
84
85 _, err := e.E.AddPolicies([][]string{
86 {"server:owner", spindle, spindle, "server:invite"},
87 })
88 if err != nil {
89 return err
90 }
91
92 // all owners are also members
93 _, err = e.E.AddGroupingPolicy("server:owner", "server:member", spindle)
94 return err
95}
96
97func (e *Enforcer) RemoveSpindle(spindle string) error {
98 spindle = intoSpindle(spindle)
99 _, err := e.E.DeleteDomains(spindle)
100 return err
101}
102
103func (e *Enforcer) RemoveKnot(knot string) error {
104 _, err := e.E.DeleteDomains(knot)
105 return err
106}
107
108func (e *Enforcer) GetKnotsForUser(did string) ([]string, error) {
109 keepFunc := isNotSpindle
110 stripFunc := unSpindle
111 return e.getDomainsForUser(did, keepFunc, stripFunc)
112}
113
114func (e *Enforcer) GetSpindlesForUser(did string) ([]string, error) {
115 keepFunc := isSpindle
116 stripFunc := unSpindle
117 return e.getDomainsForUser(did, keepFunc, stripFunc)
118}
119
120func (e *Enforcer) AddKnotOwner(domain, owner string) error {
121 return e.addOwner(domain, owner)
122}
123
124func (e *Enforcer) RemoveKnotOwner(domain, owner string) error {
125 return e.removeOwner(domain, owner)
126}
127
128func (e *Enforcer) AddKnotMember(domain, member string) error {
129 return e.addMember(domain, member)
130}
131
132func (e *Enforcer) RemoveKnotMember(domain, member string) error {
133 return e.removeMember(domain, member)
134}
135
136func (e *Enforcer) AddSpindleOwner(domain, owner string) error {
137 return e.addOwner(intoSpindle(domain), owner)
138}
139
140func (e *Enforcer) RemoveSpindleOwner(domain, owner string) error {
141 return e.removeOwner(intoSpindle(domain), owner)
142}
143
144func (e *Enforcer) AddSpindleMember(domain, member string) error {
145 return e.addMember(intoSpindle(domain), member)
146}
147
148func (e *Enforcer) RemoveSpindleMember(domain, member string) error {
149 return e.removeMember(intoSpindle(domain), member)
150}
151
152func repoPolicies(member, domain, repo string) [][]string {
153 return [][]string{
154 {member, domain, repo, "repo:settings"},
155 {member, domain, repo, "repo:push"},
156 {member, domain, repo, "repo:owner"},
157 {member, domain, repo, "repo:invite"},
158 {member, domain, repo, "repo:delete"},
159 {"server:owner", domain, repo, "repo:delete"}, // server owner can delete any repo
160 }
161}
162func (e *Enforcer) AddRepo(member, domain, repo string) error {
163 err := checkRepoFormat(repo)
164 if err != nil {
165 return err
166 }
167
168 _, err = e.E.AddPolicies(repoPolicies(member, domain, repo))
169 return err
170}
171func (e *Enforcer) RemoveRepo(member, domain, repo string) error {
172 err := checkRepoFormat(repo)
173 if err != nil {
174 return err
175 }
176
177 _, err = e.E.RemovePolicies(repoPolicies(member, domain, repo))
178 return err
179}
180
181var (
182 collaboratorPolicies = func(collaborator, domain, repo string) [][]string {
183 return [][]string{
184 {collaborator, domain, repo, "repo:collaborator"},
185 {collaborator, domain, repo, "repo:settings"},
186 {collaborator, domain, repo, "repo:push"},
187 }
188 }
189)
190
191func (e *Enforcer) AddCollaborator(collaborator, domain, repo string) error {
192 err := checkRepoFormat(repo)
193 if err != nil {
194 return err
195 }
196
197 _, err = e.E.AddPolicies(collaboratorPolicies(collaborator, domain, repo))
198 return err
199}
200
201func (e *Enforcer) RemoveCollaborator(collaborator, domain, repo string) error {
202 err := checkRepoFormat(repo)
203 if err != nil {
204 return err
205 }
206
207 _, err = e.E.RemovePolicies(collaboratorPolicies(collaborator, domain, repo))
208 return err
209}
210
211func (e *Enforcer) GetUserByRole(role, domain string) ([]string, error) {
212 var membersWithoutRoles []string
213
214 // this includes roles too, casbin does not differentiate.
215 // the filtering criteria is to remove strings not starting with `did:`
216 members, err := e.E.GetImplicitUsersForRole(role, domain)
217 for _, m := range members {
218 if strings.HasPrefix(m, "did:") {
219 membersWithoutRoles = append(membersWithoutRoles, m)
220 }
221 }
222 if err != nil {
223 return nil, err
224 }
225
226 slices.Sort(membersWithoutRoles)
227 return slices.Compact(membersWithoutRoles), nil
228}
229
230func (e *Enforcer) GetKnotUsersByRole(role, domain string) ([]string, error) {
231 return e.GetUserByRole(role, domain)
232}
233
234func (e *Enforcer) GetSpindleUsersByRole(role, domain string) ([]string, error) {
235 return e.GetUserByRole(role, intoSpindle(domain))
236}
237
238func (e *Enforcer) GetUserByRoleInRepo(role, domain, repo string) ([]string, error) {
239 var users []string
240
241 policies, err := e.E.GetImplicitUsersForResourceByDomain(repo, domain)
242 for _, p := range policies {
243 user := p[0]
244 if strings.HasPrefix(user, "did:") {
245 users = append(users, user)
246 }
247 }
248 if err != nil {
249 return nil, err
250 }
251
252 slices.Sort(users)
253 return slices.Compact(users), nil
254}
255
256func (e *Enforcer) IsKnotOwner(user, domain string) (bool, error) {
257 return e.isRole(user, "server:owner", domain)
258}
259
260func (e *Enforcer) IsKnotMember(user, domain string) (bool, error) {
261 return e.isRole(user, "server:member", domain)
262}
263
264func (e *Enforcer) IsSpindleOwner(user, domain string) (bool, error) {
265 return e.isRole(user, "server:owner", intoSpindle(domain))
266}
267
268func (e *Enforcer) IsSpindleMember(user, domain string) (bool, error) {
269 return e.isRole(user, "server:member", intoSpindle(domain))
270}
271
272func (e *Enforcer) IsKnotInviteAllowed(user, domain string) (bool, error) {
273 return e.isInviteAllowed(user, domain)
274}
275
276func (e *Enforcer) IsSpindleInviteAllowed(user, domain string) (bool, error) {
277 return e.isInviteAllowed(user, intoSpindle(domain))
278}
279
280func (e *Enforcer) IsRepoCreateAllowed(user, domain string) (bool, error) {
281 return e.E.Enforce(user, domain, domain, "repo:create")
282}
283
284func (e *Enforcer) IsRepoDeleteAllowed(user, domain, repo string) (bool, error) {
285 return e.E.Enforce(user, domain, repo, "repo:delete")
286}
287
288func (e *Enforcer) IsRepoOwner(user, domain, repo string) (bool, error) {
289 return e.E.Enforce(user, domain, repo, "repo:owner")
290}
291
292func (e *Enforcer) IsRepoCollaborator(user, domain, repo string) (bool, error) {
293 return e.E.Enforce(user, domain, repo, "repo:collaborator")
294}
295
296func (e *Enforcer) IsPushAllowed(user, domain, repo string) (bool, error) {
297 return e.E.Enforce(user, domain, repo, "repo:push")
298}
299
300func (e *Enforcer) IsSettingsAllowed(user, domain, repo string) (bool, error) {
301 return e.E.Enforce(user, domain, repo, "repo:settings")
302}
303
304func (e *Enforcer) IsCollaboratorInviteAllowed(user, domain, repo string) (bool, error) {
305 return e.E.Enforce(user, domain, repo, "repo:invite")
306}
307
308// given a repo, what permissions does this user have? repo:owner? repo:invite? etc.
309func (e *Enforcer) GetPermissionsInRepo(user, domain, repo string) []string {
310 var permissions []string
311 res := e.E.GetPermissionsForUserInDomain(user, domain)
312 for _, p := range res {
313 // get only permissions for this resource/repo
314 if p[2] == repo {
315 permissions = append(permissions, p[3])
316 }
317 }
318
319 return permissions
320}