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}