permissions_basic.gno
4.69 Kb ยท 172 lines
1package boards2
2
3import (
4 "gno.land/p/nt/avl"
5 "gno.land/p/nt/commondao"
6)
7
8// ValidatorFunc defines a function type for permissions validators.
9type ValidatorFunc func(Permissions, Args) error
10
11// BasicPermissions manages users, roles and permissions.
12type BasicPermissions struct {
13 superRole Role
14 dao *commondao.CommonDAO
15 users *avl.Tree // string(address) -> []Role
16 roles *avl.Tree // string(role) -> []Permission
17 validators *avl.Tree // string(Permission) -> BasicPermissionValidator
18}
19
20// NewBasicPermissions creates a new permissions type.
21// This type is a default implementation to handle users, roles and permissions.
22func NewBasicPermissions(dao *commondao.CommonDAO) *BasicPermissions {
23 if dao == nil {
24 panic("basic permissions require a DAO")
25 }
26
27 return &BasicPermissions{
28 dao: dao,
29 roles: avl.NewTree(),
30 users: avl.NewTree(),
31 validators: avl.NewTree(),
32 }
33}
34
35// ValidateFunc add a validator function for a permission.
36func (bp *BasicPermissions) ValidateFunc(p Permission, fn ValidatorFunc) {
37 bp.validators.Set(string(p), fn)
38}
39
40// SetSuperRole assigns a super role.
41// A super role is one that have all permissions.
42// These type of role doesn't need to be mapped to any permission.
43func (bp *BasicPermissions) SetSuperRole(r Role) {
44 bp.superRole = r
45}
46
47// AddRole add a role with one or more assigned permissions.
48func (bp *BasicPermissions) AddRole(r Role, p Permission, extra ...Permission) {
49 bp.roles.Set(string(r), append([]Permission{p}, extra...))
50}
51
52// RoleExists checks if a role exists.
53func (bp BasicPermissions) RoleExists(r Role) bool {
54 return (bp.superRole != "" && r == bp.superRole) || bp.roles.Has(string(r))
55}
56
57// GetUserRoles returns the list of roles assigned to a user.
58func (bp BasicPermissions) GetUserRoles(user address) []Role {
59 v, found := bp.users.Get(user.String())
60 if !found {
61 return nil
62 }
63 return v.([]Role)
64}
65
66// HasRole checks if a user has a specific role assigned.
67func (bp BasicPermissions) HasRole(user address, r Role) bool {
68 for _, role := range bp.GetUserRoles(user) {
69 if role == r {
70 return true
71 }
72 }
73 return false
74}
75
76// HasPermission checks if a user has a specific permission.
77func (bp BasicPermissions) HasPermission(user address, perm Permission) bool {
78 for _, r := range bp.GetUserRoles(user) {
79 if bp.superRole == r {
80 return true
81 }
82
83 v, found := bp.roles.Get(string(r))
84 if !found {
85 continue
86 }
87
88 for _, p := range v.([]Permission) {
89 if p == perm {
90 return true
91 }
92 }
93 }
94 return false
95}
96
97// SetUserRoles adds a new user when it doesn't exist and sets its roles.
98// Method can also be called to change the roles of an existing user.
99// All user's roles can be removed by calling this method without roles.
100func (bp *BasicPermissions) SetUserRoles(_ realm, user address, roles ...Role) {
101 if !bp.HasUser(user) {
102 bp.dao.Members().Add(user)
103 }
104
105 for _, r := range roles {
106 if !bp.RoleExists(r) {
107 panic("invalid role: " + string(r))
108 }
109 }
110
111 bp.users.Set(user.String(), append([]Role(nil), roles...))
112}
113
114// RemoveUser removes a user from permissions.
115func (bp *BasicPermissions) RemoveUser(_ realm, user address) bool {
116 _, removed := bp.users.Remove(user.String())
117 bp.dao.Members().Remove(user)
118 return removed
119}
120
121// HasUser checks if a user exists.
122func (bp BasicPermissions) HasUser(user address) bool {
123 return bp.dao.Members().Has(user)
124}
125
126// UsersCount returns the total number of users the permissioner contains.
127func (bp BasicPermissions) UsersCount() int {
128 return bp.dao.Members().Size()
129}
130
131// IterateUsers iterates permissions' users.
132func (bp BasicPermissions) IterateUsers(start, count int, fn UsersIterFn) (stopped bool) {
133 bp.dao.Members().IterateByOffset(start, count, func(addr address) bool {
134 roles := bp.GetUserRoles(addr)
135 stopped = fn(User{
136 Address: addr,
137 Roles: roles,
138 })
139 return stopped
140 })
141 return
142}
143
144// WithPermission calls a callback when a user has a specific permission.
145// It panics on error or when a handler panics.
146// Callbacks are by default called when there is no handle registered for the permission.
147func (bp *BasicPermissions) WithPermission(_ realm, user address, p Permission, args Args, cb func(realm)) {
148 if !bp.HasPermission(user, p) || !bp.dao.Members().Has(user) {
149 panic("unauthorized")
150 }
151
152 // Execute custom validation before calling the callback
153 v, found := bp.validators.Get(string(p))
154 if found {
155 err := v.(ValidatorFunc)(bp, args)
156 if err != nil {
157 panic(err)
158 }
159 }
160
161 cb(cross)
162}
163
164func createBasicPermissions(owners ...address) *BasicPermissions {
165 perms := NewBasicPermissions(commondao.New())
166 perms.SetSuperRole(RoleOwner)
167 perms.AddRole(RoleAdmin, PermissionBoardCreate)
168 for _, owner := range owners {
169 perms.SetUserRoles(cross, owner, RoleOwner)
170 }
171 return perms
172}