Tenant scope verified via failure.submission.organisation_id, NOT route
binding. Cross-tenant access returns false (controllers in sessions 2/3
will translate to 404 to prevent enumeration). Five abilities:
viewAny, view, retry, resolve, dismiss.
Laravel 12 auto-discovers App\Policies\FormBuilder\FormSubmissionActionFailurePolicy
for App\Models\FormBuilder\FormSubmissionActionFailure — no explicit
registration needed (pattern matches the existing FormSubmissionPolicy).
IDOR-class security tests included with explicit RFC V3 cross-reference
in the test class docblock.
Refs: RFC-WS-6.md §4 (V3), ARCH-FORM-BUILDER.md §22.9
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 3 of S2b. Six policies and fifteen form requests for the universal
form builder. Every exists: rule is scoped to the route's organisation
or form_schema to close the A01-5..18 findings from SECURITY_AUDIT.md.
Policies (api/app/Policies/FormBuilder/):
- FormSchemaPolicy, FormFieldPolicy, FormFieldLibraryPolicy,
FormTemplatePolicy, FormSubmissionPolicy, FormSchemaWebhookPolicy.
- FormSubmissionPolicy honours subject-self (user / person.user_id
match / submitted_by_user_id) and active delegations, per §18.3.
- No `return true` placeholders — each method checks org membership and
role via Spatie's hasRole().
Form Requests (api/app/Http/Requests/Api/V1/FormBuilder/):
- Schema: Store/UpdateFormSchemaRequest, RotatePublicTokenRequest.
- Fields: Store/UpdateFormFieldRequest, ReorderFormFieldsRequest (field
ids scoped to the route schema), InsertLibraryFieldRequest (library
scoped to the route organisation).
- Templates: Store/UpdateFormTemplateRequest.
- Field library: Store/UpdateFormFieldLibraryRequest.
- Submissions: CreateFormSubmissionRequest, UpsertFormValuesRequest
(slug allow-list derived from schema), SubmitFormSubmissionRequest,
ReviewFormSubmissionRequest, DelegateFormSubmissionRequest (delegatee
scoped to organisation pivot).
- Webhooks: Store/UpdateFormSchemaWebhookRequest.
- Public: PublicSubmissionRequest (captcha_token collected here,
enforcement in controller per config('form_builder.captcha')).
All enum validation routes through the existing PHP enums from S1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>