package chunk import ( "chain" "chain/runtime" "strconv" "strings" ) const ( CreatePermissionEvent = "CreatePermission" DeletePermissionEvent = "DeletePermission" CreateRoleEvent = "CreateRole" UpdateRoleEvent = "UpdateRole" UpdateRolePropertyEvent = "UpdateRoleProperty" DeleteRoleEvent = "DeleteRole" AddPermissionEvent = "AddPermission" RemovePermissionEvent = "RemovePermission" AddGrantableEvent = "AddGrantable" RemoveGrantableEvent = "RemoveGrantable" GrantRoleEvent = "GrantRole" RevokeRoleEvent = "RevokeRole" ) var ( // Permission registry: permission name -> description permissionMap = make(map[string]string) // Role registry: role name -> role info (name, maxAssign) roleMap = make(map[string]map[string]string) // Role permissions: role name -> permission -> bool rolePermissions = make(map[string]map[string]bool) // Grantable map: granterRole -> grantableRole -> bool grantableMap = make(map[string]map[string]bool) ) func init() { // Initialize core permissions permissionMap["block:install"] = "Install blocks in chunk" permissionMap["block:uninstall"] = "Uninstall blocks from chunk" permissionMap["role:grant"] = "Grant roles to users" permissionMap["role:revoke"] = "Revoke roles from users" // Initialize default "editor" role (unlimited assignments) roleMap["editor"] = map[string]string{ "name": "editor", "maxAssign": "0", // 0 = unlimited } rolePermissions["editor"] = map[string]bool{ "block:install": true, "block:uninstall": true, } } // ==================== Permission Management (Admin Only) ==================== // CreatePermission creates a new permission definition func CreatePermission(cur realm, name string, description string) { caller := runtime.PreviousRealm().Address() assertIsAdmin(caller) if _, found := permissionMap[name]; found { panic("permission already exists: " + name) } permissionMap[name] = description chain.Emit(CreatePermissionEvent, "name", name, "description", description) } // DeletePermission deletes a permission and removes it from all roles func DeletePermission(cur realm, name string) { caller := runtime.PreviousRealm().Address() assertIsAdmin(caller) if _, found := permissionMap[name]; !found { panic("permission not found: " + name) } // Remove from all roles for roleName := range roleMap { if perms, found := rolePermissions[roleName]; found { delete(perms, name) } } delete(permissionMap, name) chain.Emit(DeletePermissionEvent, "name", name) } // AddPermissionToRole adds a permission to an existing role func AddPermissionToRole(cur realm, roleName string, permissionName string) { caller := runtime.PreviousRealm().Address() assertIsAdmin(caller) if _, found := roleMap[roleName]; !found { panic("role not found: " + roleName) } if _, found := permissionMap[permissionName]; !found { panic("permission not found: " + permissionName) } perms := rolePermissions[roleName] if perms == nil { perms = make(map[string]bool) rolePermissions[roleName] = perms } if perms[permissionName] { panic("permission '" + permissionName + "' already in role '" + roleName + "'") } perms[permissionName] = true chain.Emit(AddPermissionEvent, "role", roleName, "permission", permissionName) } // RemovePermissionFromRole removes a permission from a role func RemovePermissionFromRole(cur realm, roleName string, permissionName string) { caller := runtime.PreviousRealm().Address() assertIsAdmin(caller) if _, found := roleMap[roleName]; !found { panic("role not found: " + roleName) } if _, found := permissionMap[permissionName]; !found { panic("permission not found: " + permissionName) } perms := rolePermissions[roleName] if perms == nil || !perms[permissionName] { panic("permission '" + permissionName + "' not in role '" + roleName + "'") } delete(perms, permissionName) chain.Emit(RemovePermissionEvent, "role", roleName, "permission", permissionName) } // ==================== Role Management (Admin Only) ==================== // CreateRole creates a new role with permissions // permissions is a comma-separated string of permission names // maxAssign: 0 = unlimited assignments func CreateRole(cur realm, roleName string, maxAssign int, permissions string) { caller := runtime.PreviousRealm().Address() assertIsAdmin(caller) if _, found := roleMap[roleName]; found { panic("role already exists: " + roleName) } if maxAssign < 0 { panic("maxAssign must be non-negative") } // Parse and validate permissions perms := make(map[string]bool) if len(permissions) > 0 { permList := strings.Split(permissions, ",") for _, perm := range permList { perm = strings.TrimSpace(perm) if len(perm) == 0 { continue } if _, found := permissionMap[perm]; !found { panic("permission not found: " + perm) } perms[perm] = true } } roleMap[roleName] = map[string]string{ "name": roleName, "maxAssign": strconv.Itoa(maxAssign), } rolePermissions[roleName] = perms chain.Emit(CreateRoleEvent, "name", roleName, "permissions", permissions) } // UpdateRole updates a role's maxAssign and permissions // permissions is a comma-separated string of permission names (replaces all existing permissions) // maxAssign: 0 = unlimited assignments func UpdateRole(cur realm, roleName string, maxAssign int, permissions string) { caller := runtime.PreviousRealm().Address() assertIsAdmin(caller) if _, found := roleMap[roleName]; !found { panic("role not found: " + roleName) } if maxAssign < 0 { panic("maxAssign must be non-negative") } // Parse and validate permissions perms := make(map[string]bool) if len(permissions) > 0 { permList := strings.Split(permissions, ",") for _, perm := range permList { perm = strings.TrimSpace(perm) if len(perm) == 0 { continue } if _, found := permissionMap[perm]; !found { panic("permission not found: " + perm) } perms[perm] = true } } roleMap[roleName]["maxAssign"] = strconv.Itoa(maxAssign) rolePermissions[roleName] = perms chain.Emit(UpdateRoleEvent, "name", roleName, "maxAssign", strconv.Itoa(maxAssign), "permissions", permissions) } // UpdateRoleProperty updates a single property of a role func UpdateRoleProperty(cur realm, roleName string, key string, value string) { caller := runtime.PreviousRealm().Address() assertIsAdmin(caller) if key == "name" { panic("reserved key: name") } roleInfo, found := roleMap[roleName] if !found { panic("role not found: " + roleName) } roleInfo[key] = value chain.Emit(UpdateRolePropertyEvent, "name", roleName, "key", key, "value", value) } // DeleteRole deletes a role and removes all its assignments func DeleteRole(cur realm, roleName string) { caller := runtime.PreviousRealm().Address() assertIsAdmin(caller) if _, found := roleMap[roleName]; !found { panic("role not found: " + roleName) } // Remove role from all chunks for tid := range chunkAuthorization { for addr := range chunkAuthorization[tid] { delete(chunkAuthorization[tid][addr], roleName) if len(chunkAuthorization[tid][addr]) == 0 { delete(chunkAuthorization[tid], addr) } } if len(chunkAuthorization[tid]) == 0 { delete(chunkAuthorization, tid) } } // Remove from grantable map delete(grantableMap, roleName) for _, grantables := range grantableMap { delete(grantables, roleName) } delete(roleMap, roleName) delete(rolePermissions, roleName) chain.Emit(DeleteRoleEvent, "name", roleName) } // ==================== Grantable Management (Admin Only) ==================== // AddGrantable allows granterRole to grant grantableRole to users func AddGrantable(cur realm, granterRole, grantableRole string) { caller := runtime.PreviousRealm().Address() assertIsAdmin(caller) if _, found := roleMap[granterRole]; !found { panic("granter role not found: " + granterRole) } if _, found := roleMap[grantableRole]; !found { panic("grantable role not found: " + grantableRole) } if _, found := grantableMap[granterRole]; !found { grantableMap[granterRole] = make(map[string]bool) } grantableMap[granterRole][grantableRole] = true chain.Emit(AddGrantableEvent, "granterRole", granterRole, "grantableRole", grantableRole) } // RemoveGrantable removes the ability for granterRole to grant grantableRole func RemoveGrantable(cur realm, granterRole, grantableRole string) { caller := runtime.PreviousRealm().Address() assertIsAdmin(caller) if _, found := roleMap[granterRole]; !found { panic("granter role not found: " + granterRole) } if _, found := roleMap[grantableRole]; !found { panic("grantable role not found: " + grantableRole) } grantables, found := grantableMap[granterRole] if !found || !grantables[grantableRole] { panic("role hierarchy not found: " + granterRole + " -> " + grantableRole) } delete(grantables, grantableRole) if len(grantables) == 0 { delete(grantableMap, granterRole) } chain.Emit(RemoveGrantableEvent, "granterRole", granterRole, "grantableRole", grantableRole) } // ==================== Query Functions ==================== // GetPermissions returns all available permissions func GetPermissions() map[string]string { return permissionMap } // GetRoleInfo returns detailed information about a role func GetRoleInfo(roleName string) map[string]string { if roleInfo, found := roleMap[roleName]; found { return roleInfo } return nil } // ListRoles returns all available role names func ListRoles() []string { result := []string{} for roleName := range roleMap { result = append(result, roleName) } return result } // ListGrantables returns roles that granterRole can grant func ListGrantables(granterRole string) []string { result := []string{} if grantables, found := grantableMap[granterRole]; found { for role := range grantables { result = append(result, role) } } return result }