Skip to content

Commit

Permalink
feat: gno RoleManager (#1293)
Browse files Browse the repository at this point in the history
* feat: role manager V2

* feat: add all permission

* feat: follow ressource:pattern & split the init user from the getter

* feat: follow ressource:pattern & split the init user from the getter

* feat: follow ressource:pattern & split the init user from the getter

* feat: add Owner method to get the owner

* feat: role manager apply reviews

* fix: return false instead of panic in has role/permission

* fix: return false instead of panic in has role/permission

---------

Co-authored-by: n0izn0iz <[email protected]>
  • Loading branch information
MikaelVallenet and n0izn0iz authored Oct 7, 2024
1 parent ff987c8 commit 2cbb12a
Show file tree
Hide file tree
Showing 4 changed files with 436 additions and 2 deletions.
7 changes: 7 additions & 0 deletions gno/p/role_manager/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module gno.land/p/teritori/role_manager

require (
gno.land/p/demo/avl v0.0.0-latest
gno.land/p/demo/ownable v0.0.0-latest
gno.land/p/demo/testutils v0.0.0-latest
)
180 changes: 180 additions & 0 deletions gno/p/role_manager/role_manager.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package role_manager

import (
"std"

"gno.land/p/demo/avl"
"gno.land/p/demo/ownable"
)

const (
PermissionAll = "*"
PermissionWriteRole = "role:write"
)

type Role struct {
name string
permissions *avl.Tree // permission -> struct{}
users *avl.Tree // user addr -> struct{}
}

type RoleManager struct {
owner *ownable.Ownable
roles *avl.Tree // role name -> Role
users *avl.Tree // user addr -> role[]
}

func NewWithAddress(addr std.Address) *RoleManager {
rm := &RoleManager{
owner: ownable.NewWithAddress(addr),
roles: avl.NewTree(),
users: avl.NewTree(),
}

return rm
}

func (rm *RoleManager) GetOwner() *ownable.Ownable {
return rm.owner
}

func (rm *RoleManager) CreateNewRole(roleName string, permissions []string) {
caller := std.PrevRealm().Addr()
if rm.owner.Owner() != caller && !rm.HasPermission(caller, PermissionWriteRole) {
panic("caller does not have permission")
}

if rm.roles.Has(roleName) {
panic("role already exists")
}

role := &Role{
name: roleName,
permissions: avl.NewTree(),
users: avl.NewTree(),
}

for _, permission := range permissions {
role.permissions.Set(permission, struct{}{})
}

rm.roles.Set(roleName, role)
}

func (rm *RoleManager) DeleteRole(roleName string) {
caller := std.PrevRealm().Addr()
if rm.owner.Owner() != caller && !rm.HasPermission(caller, PermissionWriteRole) {
panic("caller does not have permission")
}

if !rm.roles.Has(roleName) {
panic("role does not exist")
}

role := rm.mustGetRole(roleName)
role.users.Iterate("", "", func(key string, value interface{}) bool {
user := std.Address(key)
userRoles := rm.getUser(user)
userRoles.Remove(roleName)
return false
})
rm.roles.Remove(roleName)
}

func (rm *RoleManager) AddPermissionToRole(permission string, roleName string) {
caller := std.PrevRealm().Addr()
if rm.owner.Owner() != caller && !rm.HasPermission(caller, PermissionWriteRole) {
panic("caller does not have permission")
}

role := rm.mustGetRole(roleName)
role.permissions.Set(permission, struct{}{})
}

func (rm *RoleManager) RemovePermissionFromRole(permission string, roleName string) {
caller := std.PrevRealm().Addr()
if rm.owner.Owner() != caller && !rm.HasPermission(caller, PermissionWriteRole) {
panic("caller does not have permission")
}

role := rm.mustGetRole(roleName)
role.permissions.Remove(permission)
}

func (rm *RoleManager) AddRoleToUser(user std.Address, roleName string) {
caller := std.PrevRealm().Addr()
if rm.owner.Owner() != caller && !rm.HasPermission(caller, PermissionWriteRole) {
panic("caller does not have permission")
}

role := rm.mustGetRole(roleName)
userRoles := rm.getUser(user)
if userRoles == nil {
userRoles = avl.NewTree()
rm.users.Set(user.String(), userRoles)
}
userRoles.Set(roleName, role)
role.users.Set(user.String(), struct{}{})
}

func (rm *RoleManager) RemoveRoleFromUser(user std.Address, roleName string) {
caller := std.PrevRealm().Addr()
if rm.owner.Owner() != caller && !rm.HasPermission(caller, PermissionWriteRole) {
panic("caller does not have permission")
}

role := rm.mustGetRole(roleName)
userRoles := rm.getUser(user)
if userRoles == nil {
return
}
userRoles.Remove(roleName)
role.users.Remove(user.String())
if userRoles.Size() == 0 {
rm.users.Remove(user.String())
}
}

func (rm *RoleManager) HasPermission(user std.Address, permission string) bool {
userRoles := rm.getUser(user)
if userRoles == nil {
return false
}
res := false
userRoles.Iterate("", "", func(key string, value interface{}) bool {
role, ok := value.(*Role)
if !ok {
panic("role does not exist")
}
if role.permissions.Has(permission) || role.permissions.Has(PermissionAll) {
res = true
return true
}
return false
})
return res
}

func (rm *RoleManager) HasRole(user std.Address, roleName string) bool {
userRoles := rm.getUser(user)
if userRoles == nil {
return false
}
return userRoles.Has(roleName)
}

func (rm *RoleManager) mustGetRole(roleName string) *Role {
role, ok := rm.roles.Get(roleName)
if !ok {
panic("role does not exist")
}
return role.(*Role)
}

func (rm *RoleManager) getUser(addr std.Address) *avl.Tree {
user, ok := rm.users.Get(addr.String())
if !ok {
return nil
}
return user.(*avl.Tree)
}
Loading

0 comments on commit 2cbb12a

Please sign in to comment.