Commit c272e48
`bindRuleHooks` located the rows to recompute from a single record id
(`if (!id) return`), and `ObjectQL.update()` only populates `input.id` for a
scalar `where.id`. A predicate write routes to `updateMany` and carries no id,
so every bulk write skipped sharing-rule recompute entirely: records bulk-moved
out of a rule's criteria kept the `sys_record_share` rows the rule had issued,
and their recipients kept access the rules no longer implied. Fail-open on the
authorization side; same family as #4757 and #4778.
Keyed off the write's ROW SET instead of one id. `beforeUpdate`/`beforeDelete`
resolve the affected rows from the predicate and stash them on the shared hook
context (the before hook is where it must happen — the write is what makes those
rows unfindable); the after hook acts on them.
Per the maintainer's ruling (option C):
- bounded set (<= RULE_RECOMPUTE_ROW_CAP = 1000) -> per-row
`evaluateAllForRecord`, synchronous, diff-based so both directions are
covered (out of the criteria revokes, into it grants);
- unbounded set (over cap / `multi` with no `where` / failed resolve) ->
synchronous set-based revoke of the object's rule grants, then asynchronous
re-grant via `evaluateAllRulesForObject`.
The write is never refused: that would leak an internal recompute bound out as a
business limit on how many rows an admin may update. The asymmetry it trades on
is that over-granting is a security incident while under-granting is an
availability wobble, so the safety half is always synchronous and complete and
only the expensive restoration half is deferred. The re-grant is in-process
rather than routed through the OPTIONAL `IJobService`, which would make the
guarantee composition-dependent; durability comes from the plugin's existing
`kernel:bootstrapped` backfill, which re-runs the same idempotent reconcile.
Also binds `afterDelete` and retires the deleted records' rule grants (the
orphan noted at the tail of the issue). Nothing else could reach them:
`evaluateRule` iterates records that still exist, so a grant whose record is
gone outlived every reconcile path and every restart.
New on SharingRuleService: revokeRuleGrantsForObject, revokeRuleGrantsForRecords,
evaluateAllRulesForObject. Manual shares are never touched.
Claude-Session: https://claude.ai/code/session_015W6nhsDrz6zWQc8je12a1t
Co-authored-by: Claude <noreply@anthropic.com>
1 parent b40f81c commit c272e48
7 files changed
Lines changed: 1103 additions & 13 deletions
File tree
- .changeset
- packages/plugins/plugin-sharing/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
0 commit comments