Enhance dashboard resource handling and improve cedar schema validati…#1659
Enhance dashboard resource handling and improve cedar schema validati…#1659
Conversation
…on error reporting
There was a problem hiding this comment.
Pull request overview
This PR updates Cedar authorization behavior around dashboard resources and improves Cedar schema validation error reporting in the backend authorization service.
Changes:
- Adjusts how the Cedar resource type/id is derived for
dashboard:*actions, with special handling fordashboard:create. - Enhances Cedar schema validation by checking the
cedar-wasmisAuthorizedresult and surfacing validation errors as a400 Bad Request.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (action === CedarAction.DashboardCreate) { | ||
| resourceType = CedarResourceType.Connection; | ||
| resourceId = connectionId; | ||
| } else { | ||
| if (!dashboardId) return false; | ||
| resourceType = CedarResourceType.Dashboard; |
There was a problem hiding this comment.
For dashboard:create, this maps the resource to CedarResourceType.Connection, but the Cedar schema currently declares dashboard:create applies to resourceTypes: ['Dashboard'] (see cedar-schema.ts around the dashboard:create action). With Cedar enabled, isAuthorized will treat this as a schema mismatch and evaluate() will return false, causing dashboard creation to be forbidden (no legacy fallback in DashboardCreateGuard). Align the resource type/id mapping with the schema (either keep Dashboard here, or update the schema/action definition and any related entity-building/policies to make dashboard:create a Connection-scoped action).
| if (action === CedarAction.DashboardCreate) { | |
| resourceType = CedarResourceType.Connection; | |
| resourceId = connectionId; | |
| } else { | |
| if (!dashboardId) return false; | |
| resourceType = CedarResourceType.Dashboard; | |
| resourceType = CedarResourceType.Dashboard; | |
| if (action === CedarAction.DashboardCreate) { | |
| resourceId = connectionId; | |
| } else { | |
| if (!dashboardId) return false; |
| const errors = (result as unknown as { type: string; errors: string[] }).errors ?? []; | ||
| throw new HttpException( | ||
| { message: `Invalid cedar schema: ${errors.join('; ') || 'unknown validation error'}` }, |
There was a problem hiding this comment.
errors.join('; ') assumes result.errors is a string[], but elsewhere (evaluate) the code treats result.errors as a structured value and logs it via JSON.stringify. If errors is an array of objects (common for wasm error diagnostics), this will produce unhelpful output like [object Object]. Consider extracting a readable message (e.g., map each error to error.message when present, otherwise JSON.stringify(error)), and avoid the unsafe cast by narrowing on 'errors' in result or using the cedar-wasm result types.
| const errors = (result as unknown as { type: string; errors: string[] }).errors ?? []; | |
| throw new HttpException( | |
| { message: `Invalid cedar schema: ${errors.join('; ') || 'unknown validation error'}` }, | |
| const rawErrors = | |
| 'errors' in result && Array.isArray((result as any).errors) | |
| ? (result as any).errors | |
| : []; | |
| const errorMessages = rawErrors.map((err: unknown) => { | |
| if (typeof err === 'string') { | |
| return err; | |
| } | |
| if (err && typeof err === 'object' && 'message' in err && typeof (err as any).message === 'string') { | |
| return (err as any).message; | |
| } | |
| try { | |
| return JSON.stringify(err); | |
| } catch { | |
| return String(err); | |
| } | |
| }); | |
| throw new HttpException( | |
| { | |
| message: `Invalid cedar schema: ${ | |
| errorMessages.length > 0 ? errorMessages.join('; ') : 'unknown validation error' | |
| }`, | |
| }, |
…ration and enhance policy evaluation
…/rocket-admin/rocketadmin into backend_extend_cedar_permissions
…on error reporting