|
| 1 | +--- |
| 2 | +'@objectstack/plugin-security': minor |
| 3 | +--- |
| 4 | + |
| 5 | +fix(plugin-security)!: a field that declares `requiredPermissions` is not served to a caller who resolves no permission set, and that caller may not query on it or write it (#21063) |
| 6 | + |
| 7 | +Clause-②: yes (narrowing) |
| 8 | + |
| 9 | +<!-- adr-0087: not-required (no-migration-prescription) Nothing an author wrote is rewritten or changes meaning: every field-level requiredPermissions already declared mask on read and deny on write for a caller who does not hold all of it, and this change makes the runtime honour that declaration for the one caller class it skipped. No stored metadata, schema key or published type moves. --> |
| 10 | + |
| 11 | +**BREAKING for callers who resolve no permission set.** |
| 12 | + |
| 13 | +**What changed.** A field that declares `requiredPermissions` is masked on read |
| 14 | +and denied on write unless the caller holds all of them (ADR-0066 D3). A caller |
| 15 | +who carries a principal but resolves no permission set holds no capability, so |
| 16 | +the gate applies to it, but the runtime served that caller the stored value, |
| 17 | +let it filter, sort, group and aggregate on the field, and accepted a write |
| 18 | +that named it. The explain engine already reported the field hidden for that |
| 19 | +caller. Now the field is not served to it (a field that also declares a |
| 20 | +`maskingRule` is served masked, as before). A filter, sort key, group key or |
| 21 | +aggregate that names the field is refused with `403 PERMISSION_DENIED`, and so |
| 22 | +is a write payload that names it, as for any other caller who lacks the |
| 23 | +capability. |
| 24 | + |
| 25 | +The published field answers agree with what is served and refused. |
| 26 | +`ISecurityService.getReadableFields`, `getQueryableFields` and |
| 27 | +`getWritableFields` no longer list such a field for this caller, and neither |
| 28 | +does `getMetadataReadableFields` when the deployment's fallback set resolves to |
| 29 | +nothing. The write preview answers such a payload as the write path does. |
| 30 | + |
| 31 | +**Who this reaches.** A caller who resolves no permission set but carries a |
| 32 | +position, a named permission set or a user id. A caller with none of the three |
| 33 | +is handed through untouched, as before, and the field projections say so. A |
| 34 | +caller who resolves at least one permission set is unaffected, and so is a |
| 35 | +system context. Whether this caller may read or write the object at all is |
| 36 | +unchanged. |
| 37 | + |
| 38 | +**What to do.** Nothing, unless such a caller needs the field. A field's |
| 39 | +`requiredPermissions` name the capabilities that open it, so give the caller a |
| 40 | +permission set that holds all of them, or drop the requirement from the field. |
0 commit comments