feat(12.1-01): cabana.ForbiddenError answers a refused write with 403

- hooks and bulk, record, toolbar and widget actions may return it
- 403 forbidden with the localized message and field details; the write's
  transaction is rolled back; other errors stay the opaque 500
- form shows a refused save as a persistent banner and keeps the values;
  a refused delete is a toast
- smoke tests, OpenAPI notes, dist, README, docs
This commit is contained in:
Jakub Zych
2026-10-04 23:53:34 +02:00
parent 61d5fc72ad
commit 71073bc8a2
23 changed files with 486 additions and 48 deletions

View File

@@ -371,7 +371,7 @@ func AdminList() {}
// @Param X-Session-Key header string false "Form session key: the save attaches the files uploaded under it"
// @Success 201 {object} RecordEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope "also returned when controller code refuses the write; details may name fields"
// @Failure 422 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Router /{vendor}/{plugin}/{controller} [post]
@@ -390,7 +390,7 @@ func AdminCreate() {}
// @Param body body AdminIDsRequest true "Record ids"
// @Success 200 {object} Envelope[BulkResult]
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope "also returned when controller code refuses the write; details may name fields"
// @Failure 422 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
@@ -412,7 +412,7 @@ func AdminBulkDelete() {}
// @Param body body AdminIDsRequest true "Record ids"
// @Success 200 {object} Envelope[BulkActionResult]
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope "also returned when controller code refuses the write; details may name fields"
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 422 {object} ErrorEnvelope
@@ -435,7 +435,7 @@ func AdminBulkAction() {}
// @Param body body AdminActionRequest true "Empty object"
// @Success 200 {object} Envelope[AdminActionResult]
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope "also returned when controller code refuses the write; details may name fields"
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 422 {object} ErrorEnvelope
@@ -553,7 +553,7 @@ func AdminShow() {}
// @Param X-Session-Key header string false "Form session key: the save applies the file uploads and removals held against it"
// @Success 200 {object} RecordEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope "also returned when controller code refuses the write; details may name fields"
// @Failure 404 {object} ErrorEnvelope
// @Failure 422 {object} ErrorEnvelope
// @Router /{vendor}/{plugin}/{controller}/{id} [put]
@@ -571,7 +571,7 @@ func AdminUpdate() {}
// @Param id path integer true "Record id"
// @Success 200 {object} Envelope[BulkResult]
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope "also returned when controller code refuses the write; details may name fields"
// @Failure 404 {object} ErrorEnvelope
// @Failure 422 {object} ErrorEnvelope
// @Router /{vendor}/{plugin}/{controller}/{id} [delete]