Search Apps Documentation Source Content File Folder Download Copy Actions Download

authz.gno

6.70 Kb ยท 265 lines
  1package chunk
  2
  3import (
  4	"chain"
  5	"chain/runtime"
  6	"strconv"
  7	"strings"
  8
  9	"gno.land/p/demo/tokens/grc721"
 10)
 11
 12var (
 13	// Chunk authorization: tid -> address -> role name -> bool
 14	chunkAuthorization = make(map[grc721.TokenID]map[address]map[string]bool)
 15)
 16
 17// ==================== Role Grant/Revoke ====================
 18
 19// canGrant checks if an address can grant a specific role for a chunk
 20// Owner can always grant any role
 21// Users with roles that have grantable permissions can grant those roles
 22func canGrant(tid grc721.TokenID, addr address, role string) bool {
 23	// Owner can always grant any role
 24	owner := OwnerOf(tid)
 25	if owner == addr {
 26		return true
 27	}
 28
 29	// Get user's roles for this chunk
 30	if _, found := chunkAuthorization[tid]; !found {
 31		return false
 32	}
 33
 34	if _, found := chunkAuthorization[tid][addr]; !found {
 35		return false
 36	}
 37
 38	// Check if any of user's roles can grant the target role
 39	for userRole := range chunkAuthorization[tid][addr] {
 40		if grantables, found := grantableMap[userRole]; found {
 41			if grantables[role] {
 42				return true
 43			}
 44		}
 45	}
 46
 47	return false
 48}
 49
 50// GrantRole assigns a role to a user for a specific chunk
 51// Owner, admin, or users with grantable permission can grant roles
 52func GrantRole(cur realm, tid grc721.TokenID, roleName string, user address) {
 53	caller := runtime.PreviousRealm().Address()
 54
 55	// Check grant permission
 56	if getAdmin() != caller && !canGrant(tid, caller, roleName) {
 57		panic("cannot grant role: caller lacks permission to assign role '" + roleName + "'")
 58	}
 59
 60	// Check if role exists
 61	roleInfo, found := roleMap[roleName]
 62	if !found {
 63		panic("role not found: " + roleName)
 64	}
 65
 66	// Initialize maps if needed
 67	if _, found := chunkAuthorization[tid]; !found {
 68		chunkAuthorization[tid] = make(map[address]map[string]bool)
 69	}
 70
 71	if _, found := chunkAuthorization[tid][user]; !found {
 72		chunkAuthorization[tid][user] = make(map[string]bool)
 73	}
 74
 75	// Check if user already has this role
 76	if chunkAuthorization[tid][user][roleName] {
 77		panic("user " + user.String() + " already has role '" + roleName + "'")
 78	}
 79
 80	// Check MaxAssign limit (0 = unlimited)
 81	maxAssign, _ := strconv.Atoi(roleInfo["maxAssign"])
 82	if maxAssign > 0 {
 83		currentCount := 0
 84		for addr := range chunkAuthorization[tid] {
 85			if chunkAuthorization[tid][addr][roleName] {
 86				currentCount++
 87			}
 88		}
 89
 90		if currentCount >= maxAssign {
 91			panic("role assignment limit reached")
 92		}
 93	}
 94
 95	// Grant the role
 96	chunkAuthorization[tid][user][roleName] = true
 97
 98	chain.Emit(
 99		GrantRoleEvent,
100		"tid", string(tid),
101		"role", roleName,
102		"user", user.String(),
103	)
104}
105
106// RevokeRole removes a role from a user for a specific chunk
107// Owner, admin, or users with grantable permission can revoke roles
108func RevokeRole(cur realm, tid grc721.TokenID, roleName string, user address) {
109	caller := runtime.PreviousRealm().Address()
110
111	// Check revoke permission (same as grant permission)
112	if getAdmin() != caller && !canGrant(tid, caller, roleName) {
113		panic("cannot revoke role: caller lacks permission to revoke role '" + roleName + "'")
114	}
115
116	// Check if role exists
117	if _, found := roleMap[roleName]; !found {
118		panic("role not found: " + roleName)
119	}
120
121	// Check if user has the role
122	if _, found := chunkAuthorization[tid]; !found {
123		panic("user " + user.String() + " does not have role '" + roleName + "'")
124	}
125
126	if _, found := chunkAuthorization[tid][user]; !found {
127		panic("user " + user.String() + " does not have role '" + roleName + "'")
128	}
129
130	if !chunkAuthorization[tid][user][roleName] {
131		panic("user " + user.String() + " does not have role '" + roleName + "'")
132	}
133
134	// Revoke the role
135	delete(chunkAuthorization[tid][user], roleName)
136
137	// Clean up empty maps
138	if len(chunkAuthorization[tid][user]) == 0 {
139		delete(chunkAuthorization[tid], user)
140	}
141
142	if len(chunkAuthorization[tid]) == 0 {
143		delete(chunkAuthorization, tid)
144	}
145
146	chain.Emit(
147		RevokeRoleEvent,
148		"tid", string(tid),
149		"role", roleName,
150		"user", user.String(),
151	)
152}
153
154// ==================== Permission Check ====================
155
156// HasPermission checks if a user has a specific permission for a chunk
157// Owner always has all permissions
158func HasPermission(tid grc721.TokenID, user address, permission string) bool {	
159	// Admin has all permissions
160	if getAdmin() == user {
161		return true
162	}
163
164	// Master has all permissions for the world
165	worldID := getWorldIDFromTokenID(tid)
166	if IsMaster(worldID, user) {
167		return true
168	}
169
170	// Owner has all permissions
171	owner := OwnerOf(tid)
172	if owner == user {
173		return true
174	}
175
176	// Check user's roles for the permission
177	if _, found := chunkAuthorization[tid]; !found {
178		return false
179	}
180
181	if _, found := chunkAuthorization[tid][user]; !found {
182		return false
183	}
184
185	for roleName := range chunkAuthorization[tid][user] {
186		if perms, found := rolePermissions[roleName]; found {
187			if perms[permission] {
188				return true
189			}
190		}
191	}
192
193	return false
194}
195
196// HasInstallPermission checks if caller has permission to install blocks in the chunk
197func HasInstallPermission(tid grc721.TokenID, caller address) bool {
198	return HasPermission(tid, caller, "block:install")
199}
200
201// HasUninstallPermission checks if caller has permission to uninstall blocks from the chunk
202func HasUninstallPermission(tid grc721.TokenID, caller address) bool {
203	return HasPermission(tid, caller, "block:uninstall")
204}
205
206// HasRole checks if a user has a specific role for a chunk
207func HasRole(tid grc721.TokenID, user address, roleName string) bool {
208	if _, found := chunkAuthorization[tid]; !found {
209		return false
210	}
211	if _, found := chunkAuthorization[tid][user]; !found {
212		return false
213	}
214	return chunkAuthorization[tid][user][roleName]
215}
216
217// ==================== Query Functions ====================
218
219// ListUserRoles returns all roles assigned to a user for a specific chunk
220func ListUserRoles(tid grc721.TokenID, user address) []map[string]string {
221	result := []map[string]string{}
222
223	if _, found := chunkAuthorization[tid]; !found {
224		return result
225	}
226
227	if _, found := chunkAuthorization[tid][user]; !found {
228		return result
229	}
230
231	for roleName := range chunkAuthorization[tid][user] {
232		if role, found := roleMap[roleName]; found {
233			permList := []string{}
234			if perms, ok := rolePermissions[roleName]; ok {
235				for perm := range perms {
236					permList = append(permList, perm)
237				}
238			}
239			result = append(result, map[string]string{
240				"name":        role["name"],
241				"maxAssign":   role["maxAssign"],
242				"permissions": strings.Join(permList, ","),
243			})
244		}
245	}
246
247	return result
248}
249
250// ListRoleGrants returns all users assigned to a specific role for a chunk
251func ListRoleGrants(tid grc721.TokenID, roleName string) []address {
252	result := []address{}
253
254	if _, found := chunkAuthorization[tid]; !found {
255		return result
256	}
257
258	for addr := range chunkAuthorization[tid] {
259		if chunkAuthorization[tid][addr][roleName] {
260			result = append(result, addr)
261		}
262	}
263
264	return result
265}