authz_admin.gno
9.73 Kb ยท 360 lines
1package chunk
2
3import (
4 "chain"
5 "chain/runtime"
6 "strconv"
7 "strings"
8)
9
10const (
11 CreatePermissionEvent = "CreatePermission"
12 DeletePermissionEvent = "DeletePermission"
13 CreateRoleEvent = "CreateRole"
14 UpdateRoleEvent = "UpdateRole"
15 UpdateRolePropertyEvent = "UpdateRoleProperty"
16 DeleteRoleEvent = "DeleteRole"
17 AddPermissionEvent = "AddPermission"
18 RemovePermissionEvent = "RemovePermission"
19 AddGrantableEvent = "AddGrantable"
20 RemoveGrantableEvent = "RemoveGrantable"
21 GrantRoleEvent = "GrantRole"
22 RevokeRoleEvent = "RevokeRole"
23)
24
25var (
26 // Permission registry: permission name -> description
27 permissionMap = make(map[string]string)
28
29 // Role registry: role name -> role info (name, maxAssign)
30 roleMap = make(map[string]map[string]string)
31
32 // Role permissions: role name -> permission -> bool
33 rolePermissions = make(map[string]map[string]bool)
34
35 // Grantable map: granterRole -> grantableRole -> bool
36 grantableMap = make(map[string]map[string]bool)
37)
38
39func init() {
40 // Initialize core permissions
41 permissionMap["block:install"] = "Install blocks in chunk"
42 permissionMap["block:uninstall"] = "Uninstall blocks from chunk"
43 permissionMap["role:grant"] = "Grant roles to users"
44 permissionMap["role:revoke"] = "Revoke roles from users"
45
46 // Initialize default "editor" role (unlimited assignments)
47 roleMap["editor"] = map[string]string{
48 "name": "editor",
49 "maxAssign": "0", // 0 = unlimited
50 }
51 rolePermissions["editor"] = map[string]bool{
52 "block:install": true,
53 "block:uninstall": true,
54 }
55}
56
57// ==================== Permission Management (Admin Only) ====================
58
59// CreatePermission creates a new permission definition
60func CreatePermission(cur realm, name string, description string) {
61 caller := runtime.PreviousRealm().Address()
62 assertIsAdmin(caller)
63
64 if _, found := permissionMap[name]; found {
65 panic("permission already exists: " + name)
66 }
67
68 permissionMap[name] = description
69 chain.Emit(CreatePermissionEvent, "name", name, "description", description)
70}
71
72// DeletePermission deletes a permission and removes it from all roles
73func DeletePermission(cur realm, name string) {
74 caller := runtime.PreviousRealm().Address()
75 assertIsAdmin(caller)
76
77 if _, found := permissionMap[name]; !found {
78 panic("permission not found: " + name)
79 }
80
81 // Remove from all roles
82 for roleName := range roleMap {
83 if perms, found := rolePermissions[roleName]; found {
84 delete(perms, name)
85 }
86 }
87
88 delete(permissionMap, name)
89 chain.Emit(DeletePermissionEvent, "name", name)
90}
91
92// AddPermissionToRole adds a permission to an existing role
93func AddPermissionToRole(cur realm, roleName string, permissionName string) {
94 caller := runtime.PreviousRealm().Address()
95 assertIsAdmin(caller)
96
97 if _, found := roleMap[roleName]; !found {
98 panic("role not found: " + roleName)
99 }
100
101 if _, found := permissionMap[permissionName]; !found {
102 panic("permission not found: " + permissionName)
103 }
104
105 perms := rolePermissions[roleName]
106 if perms == nil {
107 perms = make(map[string]bool)
108 rolePermissions[roleName] = perms
109 }
110
111 if perms[permissionName] {
112 panic("permission '" + permissionName + "' already in role '" + roleName + "'")
113 }
114
115 perms[permissionName] = true
116 chain.Emit(AddPermissionEvent, "role", roleName, "permission", permissionName)
117}
118
119// RemovePermissionFromRole removes a permission from a role
120func RemovePermissionFromRole(cur realm, roleName string, permissionName string) {
121 caller := runtime.PreviousRealm().Address()
122 assertIsAdmin(caller)
123
124 if _, found := roleMap[roleName]; !found {
125 panic("role not found: " + roleName)
126 }
127
128 if _, found := permissionMap[permissionName]; !found {
129 panic("permission not found: " + permissionName)
130 }
131
132 perms := rolePermissions[roleName]
133 if perms == nil || !perms[permissionName] {
134 panic("permission '" + permissionName + "' not in role '" + roleName + "'")
135 }
136
137 delete(perms, permissionName)
138 chain.Emit(RemovePermissionEvent, "role", roleName, "permission", permissionName)
139}
140
141// ==================== Role Management (Admin Only) ====================
142
143// CreateRole creates a new role with permissions
144// permissions is a comma-separated string of permission names
145// maxAssign: 0 = unlimited assignments
146func CreateRole(cur realm, roleName string, maxAssign int, permissions string) {
147 caller := runtime.PreviousRealm().Address()
148 assertIsAdmin(caller)
149
150 if _, found := roleMap[roleName]; found {
151 panic("role already exists: " + roleName)
152 }
153
154 if maxAssign < 0 {
155 panic("maxAssign must be non-negative")
156 }
157
158 // Parse and validate permissions
159 perms := make(map[string]bool)
160 if len(permissions) > 0 {
161 permList := strings.Split(permissions, ",")
162 for _, perm := range permList {
163 perm = strings.TrimSpace(perm)
164 if len(perm) == 0 {
165 continue
166 }
167
168 if _, found := permissionMap[perm]; !found {
169 panic("permission not found: " + perm)
170 }
171
172 perms[perm] = true
173 }
174 }
175
176 roleMap[roleName] = map[string]string{
177 "name": roleName,
178 "maxAssign": strconv.Itoa(maxAssign),
179 }
180 rolePermissions[roleName] = perms
181
182 chain.Emit(CreateRoleEvent, "name", roleName, "permissions", permissions)
183}
184
185// UpdateRole updates a role's maxAssign and permissions
186// permissions is a comma-separated string of permission names (replaces all existing permissions)
187// maxAssign: 0 = unlimited assignments
188func UpdateRole(cur realm, roleName string, maxAssign int, permissions string) {
189 caller := runtime.PreviousRealm().Address()
190 assertIsAdmin(caller)
191
192 if _, found := roleMap[roleName]; !found {
193 panic("role not found: " + roleName)
194 }
195
196 if maxAssign < 0 {
197 panic("maxAssign must be non-negative")
198 }
199
200 // Parse and validate permissions
201 perms := make(map[string]bool)
202 if len(permissions) > 0 {
203 permList := strings.Split(permissions, ",")
204 for _, perm := range permList {
205 perm = strings.TrimSpace(perm)
206 if len(perm) == 0 {
207 continue
208 }
209
210 if _, found := permissionMap[perm]; !found {
211 panic("permission not found: " + perm)
212 }
213
214 perms[perm] = true
215 }
216 }
217
218 roleMap[roleName]["maxAssign"] = strconv.Itoa(maxAssign)
219 rolePermissions[roleName] = perms
220
221 chain.Emit(UpdateRoleEvent, "name", roleName, "maxAssign", strconv.Itoa(maxAssign), "permissions", permissions)
222}
223
224// UpdateRoleProperty updates a single property of a role
225func UpdateRoleProperty(cur realm, roleName string, key string, value string) {
226 caller := runtime.PreviousRealm().Address()
227 assertIsAdmin(caller)
228
229 if key == "name" {
230 panic("reserved key: name")
231 }
232
233 roleInfo, found := roleMap[roleName]
234 if !found {
235 panic("role not found: " + roleName)
236 }
237
238 roleInfo[key] = value
239
240 chain.Emit(UpdateRolePropertyEvent, "name", roleName, "key", key, "value", value)
241}
242
243// DeleteRole deletes a role and removes all its assignments
244func DeleteRole(cur realm, roleName string) {
245 caller := runtime.PreviousRealm().Address()
246 assertIsAdmin(caller)
247
248 if _, found := roleMap[roleName]; !found {
249 panic("role not found: " + roleName)
250 }
251
252 // Remove role from all chunks
253 for tid := range chunkAuthorization {
254 for addr := range chunkAuthorization[tid] {
255 delete(chunkAuthorization[tid][addr], roleName)
256
257 if len(chunkAuthorization[tid][addr]) == 0 {
258 delete(chunkAuthorization[tid], addr)
259 }
260 }
261
262 if len(chunkAuthorization[tid]) == 0 {
263 delete(chunkAuthorization, tid)
264 }
265 }
266
267 // Remove from grantable map
268 delete(grantableMap, roleName)
269 for _, grantables := range grantableMap {
270 delete(grantables, roleName)
271 }
272
273 delete(roleMap, roleName)
274 delete(rolePermissions, roleName)
275 chain.Emit(DeleteRoleEvent, "name", roleName)
276}
277
278// ==================== Grantable Management (Admin Only) ====================
279
280// AddGrantable allows granterRole to grant grantableRole to users
281func AddGrantable(cur realm, granterRole, grantableRole string) {
282 caller := runtime.PreviousRealm().Address()
283 assertIsAdmin(caller)
284
285 if _, found := roleMap[granterRole]; !found {
286 panic("granter role not found: " + granterRole)
287 }
288
289 if _, found := roleMap[grantableRole]; !found {
290 panic("grantable role not found: " + grantableRole)
291 }
292
293 if _, found := grantableMap[granterRole]; !found {
294 grantableMap[granterRole] = make(map[string]bool)
295 }
296
297 grantableMap[granterRole][grantableRole] = true
298 chain.Emit(AddGrantableEvent, "granterRole", granterRole, "grantableRole", grantableRole)
299}
300
301// RemoveGrantable removes the ability for granterRole to grant grantableRole
302func RemoveGrantable(cur realm, granterRole, grantableRole string) {
303 caller := runtime.PreviousRealm().Address()
304 assertIsAdmin(caller)
305
306 if _, found := roleMap[granterRole]; !found {
307 panic("granter role not found: " + granterRole)
308 }
309
310 if _, found := roleMap[grantableRole]; !found {
311 panic("grantable role not found: " + grantableRole)
312 }
313
314 grantables, found := grantableMap[granterRole]
315 if !found || !grantables[grantableRole] {
316 panic("role hierarchy not found: " + granterRole + " -> " + grantableRole)
317 }
318
319 delete(grantables, grantableRole)
320 if len(grantables) == 0 {
321 delete(grantableMap, granterRole)
322 }
323
324 chain.Emit(RemoveGrantableEvent, "granterRole", granterRole, "grantableRole", grantableRole)
325}
326
327// ==================== Query Functions ====================
328
329// GetPermissions returns all available permissions
330func GetPermissions() map[string]string {
331 return permissionMap
332}
333
334// GetRoleInfo returns detailed information about a role
335func GetRoleInfo(roleName string) map[string]string {
336 if roleInfo, found := roleMap[roleName]; found {
337 return roleInfo
338 }
339 return nil
340}
341
342// ListRoles returns all available role names
343func ListRoles() []string {
344 result := []string{}
345 for roleName := range roleMap {
346 result = append(result, roleName)
347 }
348 return result
349}
350
351// ListGrantables returns roles that granterRole can grant
352func ListGrantables(granterRole string) []string {
353 result := []string{}
354 if grantables, found := grantableMap[granterRole]; found {
355 for role := range grantables {
356 result = append(result, role)
357 }
358 }
359 return result
360}