Monorepo for Tangled
tangled.org
1package rbac_test
2
3import (
4 "database/sql"
5 "testing"
6
7 "tangled.org/core/rbac"
8
9 adapter "github.com/Blank-Xu/sql-adapter"
10 "github.com/casbin/casbin/v2"
11 "github.com/casbin/casbin/v2/model"
12 _ "github.com/mattn/go-sqlite3"
13 "github.com/stretchr/testify/assert"
14)
15
16func setup(t *testing.T) *rbac.Enforcer {
17 db, err := sql.Open("sqlite3", ":memory:?_foreign_keys=1")
18 assert.NoError(t, err)
19
20 a, err := adapter.NewAdapter(db, "sqlite3", "acl")
21 assert.NoError(t, err)
22
23 m, err := model.NewModelFromString(rbac.Model)
24 assert.NoError(t, err)
25
26 e, err := casbin.NewSyncedEnforcer(m, a)
27 assert.NoError(t, err)
28
29 e.EnableAutoSave(false)
30
31 return &rbac.Enforcer{E: e}
32}
33
34func TestAddKnotAndRoles(t *testing.T) {
35 e := setup(t)
36
37 err := e.AddKnot("example.com")
38 assert.NoError(t, err)
39
40 err = e.AddKnotOwner("example.com", "did:plc:foo")
41 assert.NoError(t, err)
42
43 isOwner, err := e.IsKnotOwner("did:plc:foo", "example.com")
44 assert.NoError(t, err)
45 assert.True(t, isOwner)
46
47 isMember, err := e.IsKnotMember("did:plc:foo", "example.com")
48 assert.NoError(t, err)
49 assert.True(t, isMember)
50}
51
52func TestAddMember(t *testing.T) {
53 e := setup(t)
54
55 err := e.AddKnot("example.com")
56 assert.NoError(t, err)
57
58 err = e.AddKnotOwner("example.com", "did:plc:foo")
59 assert.NoError(t, err)
60
61 err = e.AddKnotMember("example.com", "did:plc:bar")
62 assert.NoError(t, err)
63
64 isMember, err := e.IsKnotMember("did:plc:foo", "example.com")
65 assert.NoError(t, err)
66 assert.True(t, isMember)
67
68 isMember, err = e.IsKnotMember("did:plc:bar", "example.com")
69 assert.NoError(t, err)
70 assert.True(t, isMember)
71
72 isOwner, err := e.IsKnotOwner("did:plc:foo", "example.com")
73 assert.NoError(t, err)
74 assert.True(t, isOwner)
75
76 // negated check here
77 isOwner, err = e.IsKnotOwner("did:plc:bar", "example.com")
78 assert.NoError(t, err)
79 assert.False(t, isOwner)
80}
81
82func TestAddRepoPermissions(t *testing.T) {
83 e := setup(t)
84
85 knot := "example.com"
86
87 fooUser := "did:plc:foo"
88 fooRepo := "did:plc:foo/my-repo"
89
90 barUser := "did:plc:bar"
91 barRepo := "did:plc:bar/my-repo"
92
93 _ = e.AddKnot(knot)
94 _ = e.AddKnotMember(knot, fooUser)
95 _ = e.AddKnotMember(knot, barUser)
96
97 err := e.AddRepo(fooUser, knot, fooRepo)
98 assert.NoError(t, err)
99
100 err = e.AddRepo(barUser, knot, barRepo)
101 assert.NoError(t, err)
102
103 canPush, err := e.IsPushAllowed(fooUser, knot, fooRepo)
104 assert.NoError(t, err)
105 assert.True(t, canPush)
106
107 canPush, err = e.IsPushAllowed(barUser, knot, barRepo)
108 assert.NoError(t, err)
109 assert.True(t, canPush)
110
111 // negated
112 canPush, err = e.IsPushAllowed(barUser, knot, fooRepo)
113 assert.NoError(t, err)
114 assert.False(t, canPush)
115
116 canDelete, err := e.E.Enforce(fooUser, knot, fooRepo, "repo:delete")
117 assert.NoError(t, err)
118 assert.True(t, canDelete)
119
120 // negated
121 canDelete, err = e.E.Enforce(barUser, knot, fooRepo, "repo:delete")
122 assert.NoError(t, err)
123 assert.False(t, canDelete)
124}
125
126func TestCollaboratorPermissions(t *testing.T) {
127 e := setup(t)
128
129 knot := "example.com"
130 repo := "did:plc:foo/my-repo"
131 owner := "did:plc:foo"
132 collaborator := "did:plc:bar"
133
134 _ = e.AddKnot(knot)
135 _ = e.AddRepo(owner, knot, repo)
136
137 err := e.AddCollaborator(collaborator, knot, repo)
138 assert.NoError(t, err)
139
140 // all collaborator permissions granted
141 perms := e.GetPermissionsInRepo(collaborator, knot, repo)
142 assert.ElementsMatch(t, []string{
143 "repo:settings", "repo:push", "repo:collaborator",
144 }, perms)
145
146 err = e.RemoveCollaborator(collaborator, knot, repo)
147 assert.NoError(t, err)
148
149 // all permissions removed
150 perms = e.GetPermissionsInRepo(collaborator, knot, repo)
151 assert.ElementsMatch(t, []string{}, perms)
152}
153
154func TestGetByRole(t *testing.T) {
155 e := setup(t)
156
157 knot := "example.com"
158 repo := "did:plc:foo/my-repo"
159 owner := "did:plc:foo"
160 collaborator1 := "did:plc:bar"
161 collaborator2 := "did:plc:baz"
162
163 _ = e.AddKnot(knot)
164 _ = e.AddRepo(owner, knot, repo)
165
166 err := e.AddCollaborator(collaborator1, knot, repo)
167 assert.NoError(t, err)
168
169 err = e.AddCollaborator(collaborator2, knot, repo)
170 assert.NoError(t, err)
171
172 collaborators, err := e.GetUserByRoleInRepo("repo:collaborator", knot, repo)
173 assert.NoError(t, err)
174 assert.ElementsMatch(t, []string{
175 "did:plc:foo", // owner
176 "did:plc:bar", // collaborator1
177 "did:plc:baz", // collaborator2
178 }, collaborators)
179}
180
181func TestGetPermissionsInRepo(t *testing.T) {
182 e := setup(t)
183
184 user := "did:plc:foo"
185 knot := "example.com"
186 repo := "did:plc:foo/my-repo"
187
188 _ = e.AddKnot(knot)
189 _ = e.AddRepo(user, knot, repo)
190
191 perms := e.GetPermissionsInRepo(user, knot, repo)
192 assert.ElementsMatch(t, []string{
193 "repo:settings", "repo:push", "repo:owner", "repo:invite", "repo:delete",
194 }, perms)
195}
196
197func TestInvalidRepoFormat(t *testing.T) {
198 e := setup(t)
199
200 err := e.AddRepo("did:plc:foo", "example.com", "not-valid-format")
201 assert.Error(t, err)
202}
203
204func TestGetKnotssForUser(t *testing.T) {
205 e := setup(t)
206 _ = e.AddKnot("example.com")
207 _ = e.AddKnotOwner("example.com", "did:plc:foo")
208 _ = e.AddKnotMember("example.com", "did:plc:bar")
209
210 knots1, _ := e.GetKnotsForUser("did:plc:foo")
211 assert.Contains(t, knots1, "example.com")
212
213 knots2, _ := e.GetKnotsForUser("did:plc:bar")
214 assert.Contains(t, knots2, "example.com")
215}
216
217func TestGetKnotUsersByRole(t *testing.T) {
218 e := setup(t)
219 _ = e.AddKnot("example.com")
220 _ = e.AddKnotMember("example.com", "did:plc:foo")
221 _ = e.AddKnotOwner("example.com", "did:plc:bar")
222
223 members, _ := e.GetKnotUsersByRole("server:member", "example.com")
224 assert.Contains(t, members, "did:plc:foo")
225 assert.Contains(t, members, "did:plc:bar") // due to inheritance
226}
227
228func TestGetSpindleUsersByRole(t *testing.T) {
229 e := setup(t)
230 _ = e.AddSpindle("example.com")
231 _ = e.AddSpindleMember("example.com", "did:plc:foo")
232 _ = e.AddSpindleOwner("example.com", "did:plc:bar")
233
234 members, _ := e.GetSpindleUsersByRole("server:member", "example.com")
235 assert.Contains(t, members, "did:plc:foo")
236 assert.Contains(t, members, "did:plc:bar") // due to inheritance
237}
238
239func TestEmptyUserPermissions(t *testing.T) {
240 e := setup(t)
241 allowed, _ := e.IsPushAllowed("did:plc:nobody", "unknown.com", "did:plc:nobody/repo")
242 assert.False(t, allowed)
243}
244
245func TestDuplicatePolicyAddition(t *testing.T) {
246 e := setup(t)
247 _ = e.AddKnot("example.com")
248 _ = e.AddRepo("did:plc:foo", "example.com", "did:plc:foo/repo")
249
250 // add again
251 err := e.AddRepo("did:plc:foo", "example.com", "did:plc:foo/repo")
252 assert.NoError(t, err) // should not fail, but won't duplicate
253}
254
255func TestRemoveRepo(t *testing.T) {
256 e := setup(t)
257 repo := "did:plc:foo/repo"
258 _ = e.AddKnot("example.com")
259 _ = e.AddRepo("did:plc:foo", "example.com", repo)
260
261 allowed, _ := e.IsSettingsAllowed("did:plc:foo", "example.com", repo)
262 assert.True(t, allowed)
263
264 _ = e.RemoveRepo("did:plc:foo", "example.com", repo)
265
266 allowed, _ = e.IsSettingsAllowed("did:plc:foo", "example.com", repo)
267 assert.False(t, allowed)
268}
269
270func TestAddKnotAndSpindle(t *testing.T) {
271 e := setup(t)
272
273 err := e.AddKnot("k.com")
274 assert.NoError(t, err)
275
276 err = e.AddSpindle("s.com")
277 assert.NoError(t, err)
278
279 err = e.AddKnotOwner("k.com", "did:plc:foo")
280 assert.NoError(t, err)
281
282 err = e.AddSpindleOwner("s.com", "did:plc:foo")
283 assert.NoError(t, err)
284
285 knots, err := e.GetKnotsForUser("did:plc:foo")
286 assert.NoError(t, err)
287 assert.ElementsMatch(t, []string{
288 "k.com",
289 }, knots)
290
291 spindles, err := e.GetSpindlesForUser("did:plc:foo")
292 assert.NoError(t, err)
293 assert.ElementsMatch(t, []string{
294 "s.com",
295 }, spindles)
296}
297
298func TestAddSpindleAndRoles(t *testing.T) {
299 e := setup(t)
300
301 err := e.AddSpindle("s.com")
302 assert.NoError(t, err)
303
304 err = e.AddSpindleOwner("s.com", "did:plc:foo")
305 assert.NoError(t, err)
306
307 ok, err := e.IsSpindleOwner("did:plc:foo", "s.com")
308 assert.NoError(t, err)
309 assert.True(t, ok)
310
311 ok, err = e.IsSpindleMember("did:plc:foo", "s.com")
312 assert.NoError(t, err)
313 assert.True(t, ok)
314}
315
316func TestRemoveKnotOwner(t *testing.T) {
317 e := setup(t)
318
319 err := e.AddKnot("k.com")
320 assert.NoError(t, err)
321
322 err = e.AddKnotOwner("k.com", "did:plc:foo")
323 assert.NoError(t, err)
324
325 knots, err := e.GetKnotsForUser("did:plc:foo")
326 assert.NoError(t, err)
327 assert.ElementsMatch(t, []string{
328 "k.com",
329 }, knots)
330
331 err = e.RemoveKnotOwner("k.com", "did:plc:foo")
332 assert.NoError(t, err)
333
334 knots, err = e.GetKnotsForUser("did:plc:foo")
335 assert.NoError(t, err)
336 assert.Empty(t, knots)
337}
338
339func TestRemoveKnotMember(t *testing.T) {
340 e := setup(t)
341
342 err := e.AddKnot("k.com")
343 assert.NoError(t, err)
344
345 err = e.AddKnotOwner("k.com", "did:plc:foo")
346 assert.NoError(t, err)
347
348 err = e.AddKnotMember("k.com", "did:plc:bar")
349 assert.NoError(t, err)
350
351 knots, err := e.GetKnotsForUser("did:plc:bar")
352 assert.NoError(t, err)
353 assert.ElementsMatch(t, []string{
354 "k.com",
355 }, knots)
356
357 err = e.RemoveKnotMember("k.com", "did:plc:bar")
358 assert.NoError(t, err)
359
360 knots, err = e.GetKnotsForUser("did:plc:bar")
361 assert.NoError(t, err)
362 assert.Empty(t, knots)
363}
364
365func TestRemoveKnotRemovesRepoPolicies(t *testing.T) {
366 e := setup(t)
367
368 knot := "kelp.example"
369 owner := "did:plc:akshay"
370 collaborator := "did:plc:boltless"
371 repo := "did:plc:akshay/anemone"
372
373 assert.NoError(t, e.AddKnot(knot))
374 assert.NoError(t, e.AddKnotOwner(knot, owner))
375 assert.NoError(t, e.AddRepo(owner, knot, repo))
376 assert.NoError(t, e.AddCollaborator(collaborator, knot, repo))
377
378 isOwner, err := e.IsKnotOwner(owner, knot)
379 assert.NoError(t, err)
380 assert.True(t, isOwner)
381
382 err = e.RemoveKnot(knot)
383 assert.NoError(t, err)
384
385 isOwner, err = e.IsKnotOwner(owner, knot)
386 assert.NoError(t, err)
387 assert.False(t, isOwner)
388
389 assert.Empty(t, e.GetPermissionsInRepo(owner, knot, repo))
390 assert.Empty(t, e.GetPermissionsInRepo(collaborator, knot, repo))
391}
392
393func TestRemoveSpindleOwner(t *testing.T) {
394 e := setup(t)
395
396 err := e.AddSpindle("s.com")
397 assert.NoError(t, err)
398
399 err = e.AddSpindleOwner("s.com", "did:plc:foo")
400 assert.NoError(t, err)
401
402 spindles, err := e.GetSpindlesForUser("did:plc:foo")
403 assert.NoError(t, err)
404 assert.ElementsMatch(t, []string{
405 "s.com",
406 }, spindles)
407
408 err = e.RemoveSpindleOwner("s.com", "did:plc:foo")
409 assert.NoError(t, err)
410
411 spindles, err = e.GetSpindlesForUser("did:plc:foo")
412 assert.NoError(t, err)
413 assert.Empty(t, spindles)
414}
415
416func TestRemoveSpindleMember(t *testing.T) {
417 e := setup(t)
418
419 err := e.AddSpindle("s.com")
420 assert.NoError(t, err)
421
422 err = e.AddSpindleOwner("s.com", "did:plc:foo")
423 assert.NoError(t, err)
424
425 err = e.AddSpindleMember("s.com", "did:plc:bar")
426 assert.NoError(t, err)
427
428 spindles, err := e.GetSpindlesForUser("did:plc:foo")
429 assert.NoError(t, err)
430 assert.ElementsMatch(t, []string{
431 "s.com",
432 }, spindles)
433
434 spindles, err = e.GetSpindlesForUser("did:plc:bar")
435 assert.NoError(t, err)
436 assert.ElementsMatch(t, []string{
437 "s.com",
438 }, spindles)
439
440 err = e.RemoveSpindleMember("s.com", "did:plc:bar")
441 assert.NoError(t, err)
442
443 spindles, err = e.GetSpindlesForUser("did:plc:bar")
444 assert.NoError(t, err)
445 assert.Empty(t, spindles)
446}
447
448func TestRemoveSpindle(t *testing.T) {
449 e := setup(t)
450
451 err := e.AddSpindle("s.com")
452 assert.NoError(t, err)
453
454 err = e.AddSpindleOwner("s.com", "did:plc:foo")
455 assert.NoError(t, err)
456
457 err = e.AddSpindleMember("s.com", "did:plc:bar")
458 assert.NoError(t, err)
459
460 users, err := e.GetSpindleUsersByRole("server:member", "s.com")
461 assert.NoError(t, err)
462 assert.ElementsMatch(t, []string{
463 "did:plc:foo",
464 "did:plc:bar",
465 }, users)
466
467 err = e.RemoveSpindle("s.com")
468 assert.NoError(t, err)
469
470 // TODO: see this issue https://github.com/casbin/casbin/issues/1492
471 // s, err := e.E.GetAllDomains()
472 // assert.Empty(t, s)
473
474 spindles, err := e.GetSpindleUsersByRole("server:member", "s.com")
475 assert.NoError(t, err)
476 assert.Empty(t, spindles)
477}