assignments.gno
12.39 Kb · 453 lines
1package rbac
2
3import (
4 "gno.land/p/akkadia/v0/ds/btree"
5 "gno.land/p/akkadia/v0/ds/btreeset"
6)
7
8type assignments struct {
9 degree int
10 entityUserRoleIndex *btree.StringBTree
11 entityRoleUserIndex *btree.StringBTree
12 userRoleEntityIndex *btree.StringBTree
13}
14
15func newAssignments(degree int) *assignments {
16 return &assignments{
17 degree: degree,
18 entityUserRoleIndex: btree.NewStringBTree(degree),
19 entityRoleUserIndex: btree.NewStringBTree(degree),
20 userRoleEntityIndex: btree.NewStringBTree(degree),
21 }
22}
23
24func (as *assignments) Add(entityID string, user address, roleID uint32) {
25 as.validateEntityID(entityID)
26
27 roles := as.getOrCreateEntityUserRoleSet(entityID, user)
28 if roles.Has(roleID) {
29 panic("assignment already exists: entityID=" + entityID + ", user=" + user.String() + ", roleID=" + formatRoleID(roleID))
30 }
31 roles.Set(roleID)
32
33 users := as.getOrCreateEntityRoleUserSet(entityID, roleID)
34 users.Set(user.String())
35
36 entities := as.getOrCreateUserRoleEntitySet(user, roleID)
37 entities.Set(entityID)
38}
39
40func (as *assignments) Delete(entityID string, user address, roleID uint32) {
41 as.validateEntityID(entityID)
42
43 roles, found := as.findEntityUserRoleSet(entityID, user)
44 if !found || !roles.Has(roleID) {
45 panic("assignment not found: entityID=" + entityID + ", user=" + user.String() + ", roleID=" + formatRoleID(roleID))
46 }
47 roles.Remove(roleID)
48 as.cleanupEntityUserRoleSet(entityID, user, roles)
49
50 users, found := as.findEntityRoleUserSet(entityID, roleID)
51 if found {
52 users.Remove(user.String())
53 as.cleanupEntityRoleUserSet(entityID, roleID, users)
54 }
55
56 entities, found := as.findUserRoleEntitySet(user, roleID)
57 if found {
58 entities.Remove(entityID)
59 as.cleanupUserRoleEntitySet(user, roleID, entities)
60 }
61}
62
63func (as *assignments) Has(entityID string, user address, roleID uint32) bool {
64 as.validateEntityID(entityID)
65
66 roles, found := as.findEntityUserRoleSet(entityID, user)
67 if !found {
68 return false
69 }
70 return roles.Has(roleID)
71}
72
73func (as *assignments) DeleteRole(roleID uint32) {
74 emptyEntityRoleKeys := []string{}
75 as.entityRoleUserIndex.Iterate(nil, nil, func(entityID string, roleUsersValue any) bool {
76 roleUsers := castEntityRoleUserTree(entityID, roleUsersValue)
77 usersValue, found := roleUsers.Get(roleID)
78 if !found {
79 return false
80 }
81
82 users := castAssignmentUserSet(roleID, usersValue)
83 users.Iterate(nil, nil, func(userKey string) bool {
84 user := address(userKey)
85 if roles, found := as.findEntityUserRoleSet(entityID, user); found {
86 roles.Remove(roleID)
87 as.cleanupEntityUserRoleSet(entityID, user, roles)
88 }
89 if entities, found := as.findUserRoleEntitySet(user, roleID); found {
90 entities.Remove(entityID)
91 as.cleanupUserRoleEntitySet(user, roleID, entities)
92 }
93 return false
94 })
95
96 roleUsers.Remove(roleID)
97 if roleUsers.Size() == 0 {
98 emptyEntityRoleKeys = append(emptyEntityRoleKeys, entityID)
99 }
100 return false
101 })
102 for _, entityID := range emptyEntityRoleKeys {
103 as.entityRoleUserIndex.Remove(entityID)
104 }
105}
106
107func (as *assignments) DeleteEntity(entityID string) {
108 as.validateEntityID(entityID)
109
110 userRoles, found := as.findEntityUserRoleTree(entityID)
111 if !found {
112 as.entityRoleUserIndex.Remove(entityID)
113 return
114 }
115
116 userRoles.Iterate(nil, nil, func(userKey string, rolesValue any) bool {
117 user := address(userKey)
118 roles := castAssignmentRoleSet(entityID, rolesValue)
119 roles.Iterate(nil, nil, func(roleID uint32) bool {
120 if entities, found := as.findUserRoleEntitySet(user, roleID); found {
121 entities.Remove(entityID)
122 as.cleanupUserRoleEntitySet(user, roleID, entities)
123 }
124 return false
125 })
126 return false
127 })
128
129 as.entityUserRoleIndex.Remove(entityID)
130 as.entityRoleUserIndex.Remove(entityID)
131}
132
133func (as *assignments) ListRoles(entityID string, user address, page int, count int) []uint32 {
134 as.validateEntityID(entityID)
135 offset := pageOffset(page, count)
136
137 roles, found := as.findEntityUserRoleSet(entityID, user)
138 if !found {
139 return []uint32{}
140 }
141 return listUint32SetByOffset(roles, offset, count)
142}
143
144func (as *assignments) ListUsers(entityID string, roleID uint32, page int, count int) []address {
145 as.validateEntityID(entityID)
146 offset := pageOffset(page, count)
147
148 users, found := as.findEntityRoleUserSet(entityID, roleID)
149 if !found {
150 return []address{}
151 }
152
153 result := make([]address, 0, count)
154 users.IterateByOffset(offset, count, func(userKey string) bool {
155 result = append(result, address(userKey))
156 return false
157 })
158 return result
159}
160
161func (as *assignments) ListEntities(user address, roleID uint32, page int, count int) []string {
162 offset := pageOffset(page, count)
163
164 entities, found := as.findUserRoleEntitySet(user, roleID)
165 if !found {
166 return []string{}
167 }
168 return listStringSetByOffset(entities, offset, count)
169}
170
171func (as *assignments) Size() int {
172 size := 0
173 as.entityUserRoleIndex.Iterate(nil, nil, func(entityID string, userRolesValue any) bool {
174 userRoles := castEntityUserRoleTree(entityID, userRolesValue)
175 userRoles.Iterate(nil, nil, func(_ string, rolesValue any) bool {
176 size += castAssignmentRoleSet(entityID, rolesValue).Size()
177 return false
178 })
179 return false
180 })
181 return size
182}
183
184func (as *assignments) UserSize(entityID string, user address) int {
185 as.validateEntityID(entityID)
186
187 roles, found := as.findEntityUserRoleSet(entityID, user)
188 if !found {
189 return 0
190 }
191 return roles.Size()
192}
193
194func (as *assignments) RoleSize(entityID string, roleID uint32) int {
195 as.validateEntityID(entityID)
196
197 users, found := as.findEntityRoleUserSet(entityID, roleID)
198 if !found {
199 return 0
200 }
201 return users.Size()
202}
203
204func (as *assignments) EntitySize(user address, roleID uint32) int {
205 entities, found := as.findUserRoleEntitySet(user, roleID)
206 if !found {
207 return 0
208 }
209 return entities.Size()
210}
211
212func (as *assignments) findEntityUserRoleSet(entityID string, user address) (*btreeset.Uint32BTreeSet, bool) {
213 userRoles, found := as.findEntityUserRoleTree(entityID)
214 if !found {
215 return nil, false
216 }
217 rolesValue, found := userRoles.Get(user.String())
218 if !found {
219 return nil, false
220 }
221 return castAssignmentRoleSet(entityID, rolesValue), true
222}
223
224func (as *assignments) getOrCreateEntityUserRoleSet(entityID string, user address) *btreeset.Uint32BTreeSet {
225 userRoles := as.getOrCreateEntityUserRoleTree(entityID)
226 rolesValue, found := userRoles.Get(user.String())
227 if found {
228 return castAssignmentRoleSet(entityID, rolesValue)
229 }
230
231 roles := btreeset.NewUint32BTreeSet(as.degree)
232 userRoles.Set(user.String(), roles)
233 return roles
234}
235
236func (as *assignments) findEntityUserRoleTree(entityID string) (*btree.StringBTree, bool) {
237 userRolesValue, found := as.entityUserRoleIndex.Get(entityID)
238 if !found {
239 return nil, false
240 }
241 return castEntityUserRoleTree(entityID, userRolesValue), true
242}
243
244func (as *assignments) getOrCreateEntityUserRoleTree(entityID string) *btree.StringBTree {
245 userRoles, found := as.findEntityUserRoleTree(entityID)
246 if found {
247 return userRoles
248 }
249
250 userRoles = btree.NewStringBTree(as.degree)
251 as.entityUserRoleIndex.Set(entityID, userRoles)
252 return userRoles
253}
254
255func (as *assignments) findEntityRoleUserSet(entityID string, roleID uint32) (*btreeset.StringBTreeSet, bool) {
256 roleUsers, found := as.findEntityRoleUserTree(entityID)
257 if !found {
258 return nil, false
259 }
260 usersValue, found := roleUsers.Get(roleID)
261 if !found {
262 return nil, false
263 }
264 return castAssignmentUserSet(roleID, usersValue), true
265}
266
267func (as *assignments) getOrCreateEntityRoleUserSet(entityID string, roleID uint32) *btreeset.StringBTreeSet {
268 roleUsers := as.getOrCreateEntityRoleUserTree(entityID)
269 usersValue, found := roleUsers.Get(roleID)
270 if found {
271 return castAssignmentUserSet(roleID, usersValue)
272 }
273
274 users := btreeset.NewStringBTreeSet(as.degree)
275 roleUsers.Set(roleID, users)
276 return users
277}
278
279func (as *assignments) findEntityRoleUserTree(entityID string) (*btree.Uint32BTree, bool) {
280 roleUsersValue, found := as.entityRoleUserIndex.Get(entityID)
281 if !found {
282 return nil, false
283 }
284 return castEntityRoleUserTree(entityID, roleUsersValue), true
285}
286
287func (as *assignments) getOrCreateEntityRoleUserTree(entityID string) *btree.Uint32BTree {
288 roleUsers, found := as.findEntityRoleUserTree(entityID)
289 if found {
290 return roleUsers
291 }
292
293 roleUsers = btree.NewUint32BTree(as.degree)
294 as.entityRoleUserIndex.Set(entityID, roleUsers)
295 return roleUsers
296}
297
298func (as *assignments) findUserRoleEntitySet(user address, roleID uint32) (*btreeset.StringBTreeSet, bool) {
299 roleEntities, found := as.findUserRoleEntityTree(user)
300 if !found {
301 return nil, false
302 }
303 entitiesValue, found := roleEntities.Get(roleID)
304 if !found {
305 return nil, false
306 }
307 return castAssignmentEntitySet(roleID, entitiesValue), true
308}
309
310func (as *assignments) getOrCreateUserRoleEntitySet(user address, roleID uint32) *btreeset.StringBTreeSet {
311 roleEntities := as.getOrCreateUserRoleEntityTree(user)
312 entitiesValue, found := roleEntities.Get(roleID)
313 if found {
314 return castAssignmentEntitySet(roleID, entitiesValue)
315 }
316
317 entities := btreeset.NewStringBTreeSet(as.degree)
318 roleEntities.Set(roleID, entities)
319 return entities
320}
321
322func (as *assignments) findUserRoleEntityTree(user address) (*btree.Uint32BTree, bool) {
323 roleEntitiesValue, found := as.userRoleEntityIndex.Get(user.String())
324 if !found {
325 return nil, false
326 }
327 return castUserRoleEntityTree(user, roleEntitiesValue), true
328}
329
330func (as *assignments) getOrCreateUserRoleEntityTree(user address) *btree.Uint32BTree {
331 roleEntities, found := as.findUserRoleEntityTree(user)
332 if found {
333 return roleEntities
334 }
335
336 roleEntities = btree.NewUint32BTree(as.degree)
337 as.userRoleEntityIndex.Set(user.String(), roleEntities)
338 return roleEntities
339}
340
341func (as *assignments) cleanupEntityUserRoleSet(entityID string, user address, roles *btreeset.Uint32BTreeSet) {
342 if roles.Size() > 0 {
343 return
344 }
345 userRoles, found := as.findEntityUserRoleTree(entityID)
346 if !found {
347 return
348 }
349 userRoles.Remove(user.String())
350 if userRoles.Size() == 0 {
351 as.entityUserRoleIndex.Remove(entityID)
352 }
353}
354
355func (as *assignments) cleanupEntityRoleUserSet(entityID string, roleID uint32, users *btreeset.StringBTreeSet) {
356 if users.Size() > 0 {
357 return
358 }
359 roleUsers, found := as.findEntityRoleUserTree(entityID)
360 if !found {
361 return
362 }
363 roleUsers.Remove(roleID)
364 if roleUsers.Size() == 0 {
365 as.entityRoleUserIndex.Remove(entityID)
366 }
367}
368
369func (as *assignments) cleanupUserRoleEntitySet(user address, roleID uint32, entities *btreeset.StringBTreeSet) {
370 if entities.Size() > 0 {
371 return
372 }
373 roleEntities, found := as.findUserRoleEntityTree(user)
374 if !found {
375 return
376 }
377 roleEntities.Remove(roleID)
378 if roleEntities.Size() == 0 {
379 as.userRoleEntityIndex.Remove(user.String())
380 }
381}
382
383func (as *assignments) validateEntityID(entityID string) {
384 if entityID == "" {
385 panic("entityID required")
386 }
387}
388
389func listUint32SetByOffset(set *btreeset.Uint32BTreeSet, offset int, count int) []uint32 {
390 result := make([]uint32, 0, count)
391 set.IterateByOffset(offset, count, func(id uint32) bool {
392 result = append(result, id)
393 return false
394 })
395 return result
396}
397
398func listStringSetByOffset(set *btreeset.StringBTreeSet, offset int, count int) []string {
399 result := make([]string, 0, count)
400 set.IterateByOffset(offset, count, func(id string) bool {
401 result = append(result, id)
402 return false
403 })
404 return result
405}
406
407func castEntityUserRoleTree(entityID string, userRolesValue any) *btree.StringBTree {
408 userRoles, ok := userRolesValue.(*btree.StringBTree)
409 if !ok {
410 panic("invalid entity user role index: " + entityID)
411 }
412 return userRoles
413}
414
415func castEntityRoleUserTree(entityID string, roleUsersValue any) *btree.Uint32BTree {
416 roleUsers, ok := roleUsersValue.(*btree.Uint32BTree)
417 if !ok {
418 panic("invalid entity role user index: " + entityID)
419 }
420 return roleUsers
421}
422
423func castUserRoleEntityTree(user address, roleEntitiesValue any) *btree.Uint32BTree {
424 roleEntities, ok := roleEntitiesValue.(*btree.Uint32BTree)
425 if !ok {
426 panic("invalid user role entity index: " + user.String())
427 }
428 return roleEntities
429}
430
431func castAssignmentRoleSet(entityID string, rolesValue any) *btreeset.Uint32BTreeSet {
432 roles, ok := rolesValue.(*btreeset.Uint32BTreeSet)
433 if !ok {
434 panic("invalid user role set: " + entityID)
435 }
436 return roles
437}
438
439func castAssignmentUserSet(roleID uint32, usersValue any) *btreeset.StringBTreeSet {
440 users, ok := usersValue.(*btreeset.StringBTreeSet)
441 if !ok {
442 panic("invalid role user set: " + formatRoleID(roleID))
443 }
444 return users
445}
446
447func castAssignmentEntitySet(roleID uint32, entitiesValue any) *btreeset.StringBTreeSet {
448 entities, ok := entitiesValue.(*btreeset.StringBTreeSet)
449 if !ok {
450 panic("invalid user role entity set: " + formatRoleID(roleID))
451 }
452 return entities
453}