Skip to content
17 changes: 17 additions & 0 deletions .changeset/assignment-write-unaddressable-row-not-found.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@objectstack/plugin-security': patch
---

fix(plugin-security): an assignment write the administrator cannot address answers not-found, as a read does; a refusal names only values the caller can read

Clause-②: no
Metadata-protocol: none

An organization administrator or a delegated administrator (a caller holding an `adminScope`, an organization administrator who holds one included) updates or removes an assignment row (`sys_user_position` or `sys_user_permission_set`) by id.

- **A row the caller cannot read, and an id that names no row,** now answer `404 RECORD_NOT_FOUND`: the read door's answer for that object and id, which is what a by-id write of any other object already answers. Both used to answer `403 PERMISSION_DENIED` ("the assignment row … could not be read"). An organization administrator's edit in place is asked the same question first, so an edit of a row it reads is still refused as an edit.
- **A caller the read door refuses outright** (no read grant on the table) still gets that read refusal.
- **A refusal names a value only when the caller can read it.** A delegated administrator's `DELEGATION_SCOPE_VIOLATION` names the row's position, business unit, holder or permission set only when the caller's own read returns that column at its stored value, or when the caller wrote the value itself. Otherwise the sentence is the same without the value (for example "the assignment's position distributes a permission set which is not in the scope's allowlist"), and `details` leaves the value out. The verdict does not change: it is still taken on the stored row.
- **Unchanged.** A row the caller reads keeps its verdict and its guidance. Inserts and tenant-level administrators are judged as before, and every write that was admitted is still admitted.

For clients: a by-id update or delete of an assignment row that is gone now answers `404`, as it does on every other object.
2 changes: 1 addition & 1 deletion .changeset/delegated-admin-unreadable-row.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
'@objectstack/plugin-security': patch
---
Expand All @@ -9,7 +9,7 @@

A delegated administrator — a caller holding an `adminScope`, an organization administrator who holds one included — updates or removes an assignment row (`sys_user_position` or `sys_user_permission_set`) by id. The delegated-administration gate (ADR-0090 D12) now asks first whether the caller itself reads that row, in its own context.

- **A row the caller cannot read** is refused with exactly the answer an id that names no row gets: `403 PERMISSION_DENIED`, "the assignment row it addresses could not be read", with the same details. Both are answered by the gate; an id that names no row is no longer handed on, so a removal of one answers that refusal instead of `404 RECORD_NOT_FOUND`.
- **A row the caller cannot read** is answered exactly as an id that names no row is: the read door's `404 RECORD_NOT_FOUND` for that object and id. Both are answered at the gate, before any later step judges the write.
- **A row the caller reads** keeps the verdict and the guidance it had: the gate judges the stored row, as before.
- **A caller the read door refuses outright** (no read grant on the table) is answered as before: the read refusal, for every id the gate refuses, and the writes it was admitted are still admitted.
- **Unchanged.** Inserts, tenant-level administrators, and organization administrators who hold no scope are judged as before.
4 changes: 2 additions & 2 deletions .changeset/org-admin-removal-caller-read.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
'@objectstack/plugin-security': patch
---
Expand All @@ -9,6 +9,6 @@

An organization administrator (`organization_admin` or `organization_admin_no_bypass`) removes an assignment row (`sys_user_position` or `sys_user_permission_set`) by id. The delegated-administration gate (ADR-0090 D12) now judges that row as the administrator itself reads it, in its own context.

- **A row the administrator cannot read** is refused with exactly the answer an id that names no row gets: `403 PERMISSION_DENIED`, "the assignment row to remove could not be read", with the same details.
- **A row the administrator cannot read** is answered exactly as an id that names no row is: the read door's `404 RECORD_NOT_FOUND` for that object and id.
- **A row the administrator can read** keeps the answer it had, including the guidance that names the organization boundary.
- **Unchanged.** An edit in place is refused as before, every removal that was admitted is still admitted, and a tenant-level administrator is judged as before.
- **Unchanged.** An edit in place of a row it reads is refused as before, every removal that was admitted is still admitted, and a tenant-level administrator is judged as before.
220 changes: 190 additions & 30 deletions packages/plugins/plugin-security/src/delegated-admin-gate.ts

Large diffs are not rendered by default.

Loading
Loading