Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}