{"record":{"id":"dc00d4dda39453d4","repo":"vxcontrol/pentagi","slug":"permission-resources-view-required-to-use-resour","errorCode":null,"errorMessage":"permission 'resources.view' required to use resource IDs","messagePattern":"permission 'resources\\.view' required to use resource IDs","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/server/services/assistants.go","lineNumber":657,"sourceCode":"\t\tUpdatedAt:          database.TimeToNullTime(assistant.UpdatedAt),\n\t\tDeletedAt:          database.PtrTimeToNullTime(assistant.DeletedAt),\n\t\tModelProviderType:  database.ProviderType(assistant.ModelProviderType),\n\t\tToolCallIDTemplate: assistant.ToolCallIDTemplate,\n\t}, nil\n}\n\n// validateServiceResources fetches and validates user resource ownership for REST handlers.\n// It mirrors the logic in validateUserResources from the graph package but works with *gorm.DB.\n// privs must contain at least \"resources.view\" or \"resources.admin\"; otherwise permission is denied.\n// \"resources.admin\" bypasses the user_id ownership check.\nfunc validateServiceResources(db *gorm.DB, uid uint64, privs []string, ids []uint64) ([]database.UserResource, error) {\n\tif len(ids) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tisAdmin := slices.Contains(privs, \"resources.admin\")\n\tif !isAdmin && !slices.Contains(privs, \"resources.view\") && len(ids) > 0 {\n\t\treturn nil, fmt.Errorf(\"permission 'resources.view' required to use resource IDs\")\n\t}\n\n\tvar recs []models.UserResource\n\tif err := db.Model(&models.UserResource{}).Where(\"id IN (?)\", ids).Find(&recs).Error; err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to fetch resources: %w\", err)\n\t}\n\n\tfound := make(map[uint64]models.UserResource, len(recs))\n\tfor _, r := range recs {\n\t\tfound[r.ID] = r\n\t}\n\n\tresult := make([]database.UserResource, 0, len(ids))\n\tfor _, id := range ids {\n\t\tr, ok := found[id]\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"resource %d not found\", id)\n\t\t}","sourceCodeStart":639,"sourceCodeEnd":675,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/assistants.go#L639-L675","documentation":"validateServiceResources rejects requests that attach resource IDs to an assistant or flow when the caller's privilege set contains neither 'resources.admin' nor 'resources.view'. Only admins may bypass the view permission; regular users must hold 'resources.view' to reference any resource IDs at all. This is an authorization gate before any database lookup happens.","triggerScenarios":"Calling CreateFlowAssistant, PatchAssistant, CreateFlow, or PatchFlow with a non-empty resource ID list while the authenticated user's privileges (from their role) lack both 'resources.admin' and 'resources.view'.","commonSituations":"Deploying a flow via API with a token/role that wasn't granted resources.view; reduced-privilege service accounts created for automation; after an admin tightened role privileges and existing integrations started failing.","solutions":["Grant the 'resources.view' privilege to the user's role (insert into privileges for that role_id).","Use a role with 'resources.admin' if the integration must reference any user's resources.","Remove resource IDs from the request payload if they are not needed.","If only visibility is required for the flow, restructure the request so resources are attached later by a privileged user.","Audit the effective privileges endpoint to confirm which privileges the caller's role actually carries."],"exampleFix":"-- grant view privilege to role 2\nINSERT INTO privileges (role_id, name) VALUES (2, 'resources.view')\nON CONFLICT DO NOTHING;","handlingStrategy":"validation","validationCode":"func canUseResources(privs []string, ids []int64) error {\n    if len(ids) == 0 {\n        return nil\n    }\n    if !slices.Contains(privs, \"resources.admin\") && !slices.Contains(privs, \"resources.view\") {\n        return fmt.Errorf(\"caller lacks 'resources.view' — required to attach resource IDs\")\n    }\n    return nil\n}","typeGuard":"func hasResourceAccess(privs []string) bool {\n    return slices.Contains(privs, \"resources.admin\") || slices.Contains(privs, \"resources.view\")\n}","tryCatchPattern":"ids, err := validateServiceResources(ctx, uid, privs, rawIDs)\nif err != nil {\n    if strings.Contains(err.Error(), \"permission 'resources.view' required\") {\n        return nil, fmt.Errorf(\"your role cannot attach resources; request the 'resources.view' privilege\")\n    }\n    return nil, err\n}","preventionTips":["Check the caller's privileges before sending resource IDs","Grant 'resources.view' to automation/service roles that need resources","Keep role-privilege assignments documented per integration","Return clear 403-style messaging to clients instead of a generic error"],"tags":["authorization","permissions","resources","rbac"],"backgroundTag":"missing-permission-resources-view","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}