accesscontrol.gno
4.01 Kb ยท 205 lines
1package accesscontrol
2
3import (
4 "chain"
5 "chain/runtime"
6
7 "gno.land/p/nt/avl"
8 "gno.land/p/nt/ownable"
9)
10
11const (
12 RoleCreatedEvent = "RoleCreated"
13 RoleGrantedEvent = "RoleGranted"
14 RoleRevokedEvent = "RoleRevoked"
15 RoleRenouncedEvent = "RoleRenounced"
16 RoleSetEvent = "RoleSet"
17)
18
19// Role struct to store role information
20type Role struct {
21 Name string
22 Holders *avl.Tree // address -> struct{}
23 Ownable *ownable.Ownable
24}
25
26// Roles struct to store all Roles information
27type Roles struct {
28 Roles []*Role
29 UserToRoles avl.Tree // address -> []*Role
30 Ownable *ownable.Ownable
31}
32
33func validRoleName(name string) error {
34 if len(name) > 30 || name == "" {
35 return ErrNameRole
36 }
37 return nil
38}
39
40// NewRole creates a new instance of Role
41func NewRole(name string, admin address) (*Role, error) {
42 if err := validRoleName(name); err != nil {
43 return nil, ErrNameRole
44 }
45
46 return &Role{
47 Name: name,
48 Holders: avl.NewTree(),
49 Ownable: ownable.NewWithAddress(admin),
50 }, nil
51}
52
53// CreateRole create a new role within the realm
54func (rs *Roles) CreateRole(name string) (*Role, error) {
55 if err := validRoleName(name); err != nil {
56 return nil, ErrNameRole
57 }
58
59 if !rs.Ownable.OwnedByCurrent() {
60 return nil, ErrNotOwner
61 }
62
63 for _, role := range rs.Roles {
64 if role.Name == name {
65 return nil, ErrRoleSameName
66 }
67 }
68
69 role, err := NewRole(name, rs.Ownable.Owner())
70 if err != nil {
71 return nil, err
72 }
73
74 rs.Roles = append(rs.Roles, role)
75
76 chain.Emit(
77 RoleCreatedEvent,
78 "roleName", name,
79 "sender", rs.Ownable.Owner().String(),
80 )
81
82 return role, nil
83}
84
85// HasAccount check if an account has a specific role
86func (r *Role) HasAccount(account address) bool {
87 return r.Holders.Has(account.String())
88}
89
90// FindRole searches for a role by its name
91func (rs *Roles) FindRole(name string) (*Role, error) {
92 for _, role := range rs.Roles {
93 if role.Name == name {
94 return role, nil
95 }
96 }
97
98 return nil, ErrRoleNotFound
99}
100
101// GrantRole grants a role to an account
102func (rs *Roles) GrantRole(name string, account address) error {
103 r, err := rs.FindRole(name)
104 if err != nil {
105 return ErrRoleNotFound
106 }
107
108 if !r.Ownable.OwnedByCurrent() {
109 return ErrNotOwner
110 }
111
112 r.Holders.Set(account.String(), struct{}{})
113
114 // Add in UserToRoles
115 roles, found := rs.UserToRoles.Get(account.String())
116 if !found {
117 roles = []*Role{}
118 }
119 roles = append(roles.([]*Role), r)
120 rs.UserToRoles.Set(account.String(), roles)
121
122 chain.Emit(
123 RoleGrantedEvent,
124 "roleName", r.Name,
125 "account", account.String(),
126 "sender", runtime.CurrentRealm().Address().String(),
127 )
128
129 return nil
130}
131
132// RevokeRole revokes a role from an account
133func (rs *Roles) RevokeRole(name string, account address) error {
134 r, err := rs.FindRole(name)
135 if err != nil {
136 return ErrRoleNotFound
137 }
138
139 if !r.Ownable.OwnedByCurrent() {
140 return ErrNotOwner
141 }
142
143 r.Holders.Remove(account.String())
144
145 // Remove in UserToRoles
146 roles, found := rs.UserToRoles.Get(account.String())
147 if found {
148 updatedRoles := []*Role{}
149 for _, role := range roles.([]*Role) {
150 if role != r {
151 updatedRoles = append(updatedRoles, role)
152 }
153 }
154 rs.UserToRoles.Set(account.String(), updatedRoles)
155 }
156
157 chain.Emit(
158 RoleRevokedEvent,
159 "roleName", r.Name,
160 "account", account.String(),
161 "sender", runtime.CurrentRealm().Address().String(),
162 )
163
164 return nil
165}
166
167// RenounceRole allows an account to renounce a role it holds
168func (rs *Roles) RenounceRole(name string) error {
169 r, err := rs.FindRole(name)
170 if err != nil {
171 return ErrRoleNotFound
172 }
173
174 caller := runtime.OriginCaller()
175
176 if !r.HasAccount(caller) {
177 return ErrAccountNotRole
178 }
179
180 r.Holders.Remove(caller.String())
181
182 chain.Emit(
183 RoleRenouncedEvent,
184 "roleName", r.Name,
185 "account", caller.String(),
186 "sender", caller.String(),
187 )
188
189 return nil
190}
191
192// SetRoleAdmin transfers the ownership of the role to a new administrator
193func (r *Role) SetRoleAdmin(admin address) error {
194 if err := r.Ownable.TransferOwnership(admin); err != nil {
195 return err
196 }
197
198 chain.Emit(
199 RoleSetEvent,
200 "roleName", r.Name,
201 "newAdminRole", r.Ownable.Owner().String(),
202 )
203
204 return nil
205}