Search Apps Documentation Source Content File Folder Download Copy Actions Download

validators.gno

4.57 Kb · 184 lines
  1package permissions
  2
  3import (
  4	"chain/banker"
  5	"errors"
  6
  7	"gno.land/p/gnoland/boards"
  8	"gno.land/p/nt/ufmt/v0"
  9
 10	"gno.land/r/gnoland/boards2/v1"
 11	"gno.land/r/sys/users"
 12)
 13
 14// validateOpenBoardRename validates PermissionBoardRename.
 15//
 16// Expected `args` values:
 17// 1. Caller address
 18// 2. Board ID
 19// 3. Current board name
 20// 4. New board name
 21func validateOpenBoardRename(_ boards.Permissions, args boards.Args) error {
 22	caller, ok := args[0].(address)
 23	if !ok {
 24		return errors.New("expected a valid caller address")
 25	}
 26
 27	newName, ok := args[3].(string)
 28	if !ok {
 29		return errors.New("expected new board name to be a string")
 30	}
 31
 32	if err := checkBoardNameIsNotAddress(newName); err != nil {
 33		return err
 34	}
 35
 36	if err := checkBoardNameBelongsToAddress(caller, newName); err != nil {
 37		return err
 38	}
 39	return nil
 40}
 41
 42// validateOpenMemberInvite validates PermissionMemberInvite.
 43//
 44// Expected `args` values:
 45// 1. Caller address
 46// 2. Board ID
 47// 3. Invites
 48func validateOpenMemberInvite(perms boards.Permissions, args boards.Args) error {
 49	caller, ok := args[0].(address)
 50	if !ok {
 51		return errors.New("expected a valid caller address")
 52	}
 53
 54	invites, ok := args[2].([]boards2.Invite)
 55	if !ok {
 56		return errors.New("expected valid user invites")
 57	}
 58
 59	// Make sure that only owners invite other owners
 60	callerIsOwner := perms.HasRole(caller, boards2.RoleOwner)
 61	for _, v := range invites {
 62		if v.Role == boards2.RoleOwner && !callerIsOwner {
 63			return errors.New("only owners are allowed to invite other owners")
 64		}
 65	}
 66	return nil
 67}
 68
 69// validateOpenRoleChange validates PermissionRoleChange.
 70//
 71// Expected `args` values:
 72// 1. Caller address
 73// 2. Board ID
 74// 3. Member address
 75// 4. Role
 76func validateOpenRoleChange(perms boards.Permissions, args boards.Args) error {
 77	caller, ok := args[0].(address)
 78	if !ok {
 79		return errors.New("expected a valid caller address")
 80	}
 81
 82	// Owners and Admins can change roles.
 83	// Admins should not be able to assign or remove the Owner role from members.
 84	if perms.HasRole(caller, boards2.RoleAdmin) {
 85		role, ok := args[3].(boards.Role)
 86		if !ok {
 87			return errors.New("expected a valid member role")
 88		}
 89
 90		if role == boards2.RoleOwner {
 91			return errors.New("admins are not allowed to promote members to Owner")
 92		} else {
 93			member, ok := args[2].(address)
 94			if !ok {
 95				return errors.New("expected a valid member address")
 96			}
 97
 98			if perms.HasRole(member, boards2.RoleOwner) {
 99				return errors.New("admins are not allowed to remove the Owner role")
100			}
101		}
102	}
103	return nil
104}
105
106// validateOpenThreadCreate validates PermissionThreadCreate.
107//
108// Expected `args` values:
109// 1. Caller address
110// 2. Board ID
111// 3. Thread ID
112// 4. Title
113// 5. Body
114func validateOpenThreadCreate(perms boards.Permissions, args boards.Args) error {
115	caller, ok := args[0].(address)
116	if !ok {
117		return errors.New("expected a valid caller address")
118	}
119
120	// All board members can create threads
121	if perms.HasUser(caller) {
122		return nil
123	}
124
125	// Require non members to have some GNOT in their accounts
126	if err := checkAccountHasAmount(caller, OpenAccountAmount); err != nil {
127		return ufmt.Errorf("caller is not allowed to create threads: %s", err)
128	}
129	return nil
130}
131
132// validateOpenReplyCreate validates PermissionReplyCreate.
133//
134// Expected `args` values:
135// 1. Caller address
136// 2. Board ID
137// 3. Thread ID
138// 4. Parent ID
139// 5. Reply ID
140// 6. Body
141func validateOpenReplyCreate(perms boards.Permissions, args boards.Args) error {
142	caller, ok := args[0].(address)
143	if !ok {
144		return errors.New("expected a valid caller address")
145	}
146
147	// All board members can reply
148	if perms.HasUser(caller) {
149		return nil
150	}
151
152	// Require non members to have some GNOT in their accounts
153	if err := checkAccountHasAmount(caller, OpenAccountAmount); err != nil {
154		return ufmt.Errorf("caller is not allowed to comment: %s", err)
155	}
156	return nil
157}
158
159func checkAccountHasAmount(addr address, amount int64) error {
160	bnk := banker.NewBanker(banker.BankerTypeReadonly)
161	coins := bnk.GetCoins(addr)
162	if coins.AmountOf("ugnot") < OpenAccountAmount {
163		amount = amount / 1_000_000 // ugnot -> GNOT
164		return ufmt.Errorf("account amount is lower than %d GNOT", amount)
165	}
166	return nil
167}
168
169func checkBoardNameIsNotAddress(s string) error {
170	if address(s).IsValid() {
171		return errors.New("addresses are not allowed as board name")
172	}
173	return nil
174}
175
176func checkBoardNameBelongsToAddress(owner address, name string) error {
177	// When the board name is the name of a registered user
178	// check that caller is the owner of the name.
179	user, _ := users.ResolveName(name)
180	if user != nil && user.Addr() != owner {
181		return errors.New("board name is a user name registered to a different user")
182	}
183	return nil
184}