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}