Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}