{"record":{"id":"53e88abb8a6f5760","repo":"Tencent/WeKnora","slug":"rbac-ownership-or-role-insufficient","errorCode":null,"errorMessage":"rbac: ownership or role insufficient","messagePattern":"rbac: ownership or role insufficient","errorType":"error_code","errorClass":null,"httpStatus":403,"severity":"warning","filePath":"internal/middleware/rbac.go","lineNumber":306,"sourceCode":"\t\tc.JSON(http.StatusForbidden, gin.H{\n\t\t\t\"error\": \"Forbidden: must own the resource or have the required role\",\n\t\t})\n\t\tc.Abort()\n\t}\n}\n\n// rbacEnforcementEnabled reports whether middleware should actually\n// reject failed checks. When the flag is off the middleware still runs\n// role-only checks (logging, fast paths), but rejection is downgraded\n// to a warning and ownership lookups are skipped entirely so the dormant\n// rollout window incurs no per-request DB cost.\nfunc rbacEnforcementEnabled(cfg *config.Config) bool {\n\treturn cfg != nil && cfg.Tenant.IsRBACEnforced()\n}\n\n// ErrOwnershipForbidden is returned by EvaluateOwnershipOrRole when the\n// caller is neither the resource creator nor meets the minimum role.\nvar ErrOwnershipForbidden = errors.New(\"rbac: ownership or role insufficient\")\n\n// EvaluateOwnershipOrRole applies the same decision matrix as\n// RequireOwnershipOrRole for handlers that resolve creator_id out-of-band\n// (e.g. KB id carried in a JSON body rather than a URL param).\n//\n// Returns nil when access is allowed. ErrResourceNotFound means the\n// handler should issue its own 404. ErrOwnershipForbidden maps to 403.\n// Any other error is a transient lookup failure (503).\nfunc EvaluateOwnershipOrRole(\n\tctx context.Context,\n\tcfg *config.Config,\n\tmin types.TenantRole,\n\tcreatorID string,\n\tlookupErr error,\n) error {\n\t// API-key principals are authorized solely by the APIKeyGate (route\n\t// policy) plus the KB allow-list handlers enforce separately\n\t// (requireTenantAPIKeyKnowledgeBase(s)). Ownership (\"creator OR Admin+\")","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/middleware/rbac.go#L288-L324","documentation":"ErrOwnershipForbidden is returned by EvaluateOwnershipOrRole / RequireOwnershipOrRole when the authenticated caller is neither the resource's creator nor holds the minimum role required. Handlers like knowledge.go map it to a 403 'No permission to operate on this knowledge base'.","triggerScenarios":"A user calls a mutating endpoint (update/delete KB, etc.) where the resolved creator_id differs from the caller's user ID and the caller's role is below the configured minimum. Also via EvaluateOwnershipOrRole when creator_id is resolved out-of-band (e.g. KB id in JSON body).","commonSituations":"A collaborator trying to edit someone else's knowledge base; role downgrades leaving former admins without ownership; clients sending a KB id in the body for a resource owned by another user; RBAC enforcement newly enabled (IsRBACEnforced) changing behavior for existing callers.","solutions":["Confirm the authenticated user is actually the resource creator, or have an admin with sufficient role perform the operation.","Grant the user the required minimum role via your role-management flow if they should have access.","Verify creator_id resolution is correct — wrong KB id in the request body resolves the wrong creator.","If enforcement was recently enabled, audit role assignments before rolling out IsRBACEnforced to production."],"exampleFix":"// before: client sends KB id owned by someone else\nreq := UpdateKBRequest{ID: otherUsersKBID, Name: \"new name\"}\n// after: check ownership client-side or use an admin/service account with the required role\nkb, _ := client.GetKnowledgeBase(ctx, kbID)\nif kb.CreatorID != currentUserID && !userHasRole(ctx, \"admin\") {\n    return ErrNoPermission\n}\nclient.UpdateKnowledgeBase(ctx, req)","handlingStrategy":"type-guard","validationCode":"// before the call, confirm the user owns the resource or has the role\nkb, _ := client.GetKnowledgeBase(ctx, kbID)\nif kb.CreatorID != currentUserID && !userHasRole(ctx, minRequiredRole) {\n    return ErrNoPermission\n}","typeGuard":"func isOwnershipForbidden(err error) bool {\n    return errors.Is(err, middleware.ErrOwnershipForbidden)\n}","tryCatchPattern":"if evalErr != nil {\n    if errors.Is(evalErr, middleware.ErrOwnershipForbidden) {\n        return errors.NewForbiddenError(\"No permission to operate on this knowledge base\")\n    }\n    return evalErr\n}","preventionTips":["Check ownership/role client-side before issuing mutating requests.","Audit role assignments before enabling RBAC enforcement.","Verify the resource ID sent in the request resolves the intended creator.","Show creator info in your UI so users know which resources they can edit."],"tags":["rbac","authorization","forbidden","ownership"],"backgroundTag":"insufficient-permissions","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}