{"record":{"id":"ea7c4cfaf0102120","repo":"weaviate/weaviate","slug":"can-only-create-roles-with-less-or-equal-permissio","errorCode":null,"errorMessage":"can only create roles with less or equal permissions as the current user: %w","messagePattern":"can only create roles with less or equal permissions as the current user: %w","errorType":"exception","errorClass":null,"httpStatus":403,"severity":"warning","filePath":"adapters/handlers/rest/authz/handlers_authz.go","lineNumber":138,"sourceCode":"\tif !confinedToNamespace {\n\t\tif err = h.authorizer.Authorize(ctx, principal, authorization.VerbWithScope(originalVerb, authorization.ROLE_SCOPE_ALL), authorization.Roles(roleName)...); err == nil {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// Check if user can manage roles with matching permissions\n\tif err = h.authorizer.Authorize(ctx, principal, authorization.VerbWithScope(originalVerb, authorization.ROLE_SCOPE_MATCH), authorization.Roles(roleName)...); err == nil {\n\t\t// Verify user has all permissions they're trying to grant\n\t\tvar errs error\n\t\tfor _, policy := range policies {\n\t\t\tif err := h.authorizer.AuthorizeSilent(ctx, principal, policy.Verb, policy.Resource); err != nil {\n\t\t\t\terrs = errors.Join(errs, err)\n\t\t\t}\n\t\t}\n\t\treturn errs\n\t}\n\n\treturn fmt.Errorf(\"can only create roles with less or equal permissions as the current user: %w\", err)\n}\n\n// validateLocalRoleAssignment blocks assigning a namespace-local role unless the\n// caller is confined to that role's namespace. A local role is managed entirely\n// within its namespace, so a global operator (and any cross-namespace caller)\n// cannot assign it; global roles carry no namespace and assign anywhere. This is\n// what keeps a namespace1 role from ever reaching a namespace2 (or global)\n// subject. No-op on NS-disabled clusters, where ':' is a valid name character.\nfunc (h *authZHandlers) validateLocalRoleAssignment(principal *models.Principal, roleNames []string) error {\n\tif !h.namespacesEnabled {\n\t\treturn nil\n\t}\n\tcallerNS := namespacing.ConfinedNamespace(principal)\n\tfor _, roleName := range roleNames {\n\t\tif ns := namespacing.NamespaceFromQualified(roleName); ns != \"\" && ns != callerNS {\n\t\t\treturn fmt.Errorf(\"a namespace-local role can only be assigned by an administrator of its own namespace\")\n\t\t}\n\t}","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/weaviate/weaviate/blob/75aa4b6d11f8818305aafd4440b4e32794f7ca04/adapters/handlers/rest/authz/handlers_authz.go#L120-L156","documentation":"RBAC guard thrown by authorizeRoleScopes when a principal attempts to create or modify a role whose permissions exceed the caller's own privileges. Weaviate forbids privilege escalation: a role's combined permissions must be a subset of the current user's effective permissions. The wrapped error details which permission was excessive.","triggerScenarios":"createRole, addPermissions, removePermissions, or deleteRole called (directly or via authorizeRoleRead/resolveRoleForRead) where the target role includes a permission (action, resource type, or scope) the calling user does not itself hold.","commonSituations":"Admin holding cluster-scoped permissions tries to create a role with data-level permissions they lack; automation tokens with narrow scopes attempting to define broad roles; namespace-confined users creating global roles; permission typo introducing an action outside the caller's set.","solutions":["Grant the calling user (or its role) the permissions being assigned to the new role, then retry","Reduce the new role's permissions to a subset of the caller's own permissions","Perform role management with a root/admin-level identity","Inspect the wrapped error to identify the exact offending permission"],"exampleFix":"// before\n// caller has only collections read; creating role with objects create permission\ncreateRole(\"writer\", perms=[{action:\"create_objects\",...}]) // denied\n// after\n// first grant caller create_objects, or scope the new role to:\ncreateRole(\"reader\", perms=[{action:\"read_objects\",...}]) // subset of caller perms","handlingStrategy":"validation","validationCode":"// Go: intersect desired role permissions with the caller's own before create/modify\nmine := effectivePermissionsOf(principal)\nfor _, p := range desiredPerms {\n\tif !containsPermission(mine, p) {\n\t\treturn fmt.Errorf(\"cannot grant %v: exceeds caller's own permissions\", p)\n\t}\n}","typeGuard":"func isSubset(desired, mine []Permission) bool {\n\tset := map[Permission]bool{}\n\tfor _, m := range mine {\n\t\tset[m] = true\n\t}\n\tfor _, d := range desired {\n\t\tif !set[d] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","tryCatchPattern":"err := authz.CreateRole(ctx, name, perms)\nif err != nil && strings.Contains(err.Error(), \"less or equal permissions\") {\n\t// inspect wrapped error for the offending permission, drop it or elevate caller\n\treturn fmt.Errorf(\"role exceeds caller privileges: %v\", err)\n}","preventionTips":["Perform role management with a root/full-admin identity","Model new roles as strict subsets of the managing principal's permissions","Audit permission typos that introduce unintended actions","For namespace-confined users, create namespace-scoped roles only"],"tags":["rbac","authorization","roles","privilege-escalation"],"backgroundTag":"rbac-insufficient-permissions","analyzedSha":"75aa4b6d11f8818305aafd4440b4e32794f7ca04","analyzedAt":"2026-09-04T14:58:20.392Z","contentChangedAt":"2026-09-04T14:58:20.392Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}