fix(sharing,driver-sql): a rule authored in Setup actually applies — and switching it off actually withdraws access - #3850
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 11 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
Contributor
Author
|
The |
…and switching it off actually withdraws access (#3821) **Enabling a rule did nothing to the records already there.** Writing a `sys_sharing_rule` rebound the per-record hooks, which only makes the rule reach records written FROM THEN ON. An admin who created a rule and switched it on saw the recipient's list stay empty until somebody happened to touch each record. **Switching a rule off did not withdraw access.** Deactivating — or deleting — left every grant it had already issued in place, and boot backfill only reconciles ACTIVE rules, so those grants outlived restarts while the UI showed the rule as disabled. The reconcile existed but was reachable only through `POST /sharing/rules/:id/evaluate`, which the Console never calls and exposes no button for. Each non-system write to `sys_sharing_rule` now also reconciles that rule's grants, chained behind the existing rebind: insert/update run the same diff-based `evaluateRule` the REST endpoint runs (it purges when the rule is inactive), and delete purges directly via the new `SharingRuleService.revokeRuleGrants` — `evaluateRule` can't help there because the row is gone (`RULE_NOT_FOUND`), which is also why a rule deleted through the plain data API used to orphan its grants. Seeding and package bootstrap write with `isSystem` and are skipped; `kernel:bootstrapped` already backfills those. Reconciliation is best-effort and never fails the authoring write. **An unsortable query lost its rows, not just its order.** `SqlDriver.find()` already recovered from a SELECT projection naming a missing column; the same failure one clause over — an unknown ORDER BY column — fell through to `return []`. Because `count()` is a separate statement, the endpoint answered 200 with `records: []` and `total: 3`: the rows are there, none are shown, nothing is logged. Same family as the `$`-param footgun closed by #2926. It surfaced through the sharing-rule recipient picker, whose client mangled `'name asc'` into a list of one-character column names (fixed in objectui). Rows now outrank their order: the retry ladder drops the projection first, then the sort, then gives up — an unsortable query returns rows UNORDERED instead of empty. Non-column errors still propagate. Also refreshes the `sys_sharing_rule` help text in the zh-CN / ja-JP / es-ES bundles, which still described `recipient_type` in terms of `department` (the enum value is `business_unit`) and told admins to enter a queue name for `recipient_id` (`queue` was removed in ADR-0078). The es-ES option labels for `position` / `unit_and_subordinates` were translated as "rol". Browser-verified against the showcase Console: enabling a rule immediately gave the recipient every matching record, and switching it off took them all away. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…s it (#3821) The field `description`s on `sys_sharing_rule` render as help text under each input in Setup. They were written as notes for the next engineer: 接收访问权限的主体类型——求值时会展开为用户授权。`business_unit` 会沿 parent_business_unit_id 树展开;`team` 为扁平结构(better-auth);`position` 展开为该岗位的任职人(岗位为扁平结构,ADR-0090 D3)…(ADR-0057 D5)。 ADR numbers, table and column names, a third-party library, and enum machine values the dropdown never displays — it shows 用户 / 团队 / 业务单元 / 岗位. Several were also stale, some of them because of this very issue's UI work: `recipient_id` still said "fill in a business-unit id / team id / position name" after it became a record picker; `object_name` still said "short object name (e.g. opportunity, account)" after it became an object picker; `criteria_json` still described hand-written `FilterCondition` JSON after it became a visual builder — and carried "(FilterCondition JSON)" in its LABEL. All of it rewritten for the reader who actually sees it, in en / zh-CN / ja-JP / es-ES. The engine detail was already in the object's doc comment, which is where it stays; a comment on the first field now says so, so the next author doesn't put it back. `active` can finally state what it does — turning it off withdraws the access — which only became true with the reconcile fix in this branch. The list page's subtitle and the `managed_by` / `customized` help got the same treatment. Browser-verified in the showcase Console: no ADR, table, column, enum or library name survives anywhere in the create dialog, the detail page or the list header. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3821 (server-side half). UI half: objectstack-ai/objectui#2920.
What was broken
A rule authored in Setup did nothing to the records already there. Writing a
sys_sharing_rulerebound the per-record hooks, which only makes the rule reach records written from then on. An admin who created a rule and switched it on watched the recipient's list stay empty until somebody happened to touch each record.Switching a rule off did not withdraw access. Deactivating — or deleting — left every grant it had already issued in place, and boot backfill only reconciles ACTIVE rules, so those grants outlived restarts while the UI displayed the rule as disabled. The reconcile existed, but only behind
POST /sharing/rules/:id/evaluate, which the Console never calls and offers no button for.An unsortable query lost its rows, not just its order.
SqlDriver.find()already recovered from a SELECT projection naming a missing column; the identical failure one clause over — an unknown ORDER BY column — fell through toreturn []. Becausecount()is a separate statement, the list endpoint answered200withrecords: []andtotal: 3: the rows are there, none are shown, nothing is logged. Same family as the$-param footgun closed by #2926.The fixes
sys_sharing_rulenow reconciles that rule's grants, chained behind the existing rebind. Insert/update run the same diff-basedevaluateRulethe REST endpoint runs (it purges when the rule is inactive); delete purges via the newSharingRuleService.revokeRuleGrants, becauseevaluateRulethrowsRULE_NOT_FOUNDonce the row is gone — which is also why a rule deleted through the plain data API used to orphan its grants. Seeding / package bootstrap write withisSystemand are skipped (kernel:bootstrappedbackfills those). Best-effort: a failed reconcile logs and never fails the authoring write.sys_sharing_rulehelp text refreshed — it still describedrecipient_typein terms ofdepartment(the enum value isbusiness_unit) and told admins to enter a queue name forrecipient_id(queuewas removed in ADR-0078); es-ES renderedposition/unit_and_subordinatesas "rol".Verification
Browser-driven against
examples/app-showcase— all data, accounts and rules created through the Console UI:{"pinned":true}on an owner-private object granted exactly the matching recordsread→ readable, write refused;edit→ writable and saves$orderby=nosuchfield ascnow returns 3 rows instead of 0Tests:
plugin-sharing108 ✓ (7 new reconcile regressions),driver-sql407 ✓ (6 new recovery regressions). Both new suites verified to fail without their fix.Known gap, not fixed here
access_level: 'full'is declared but not distinct — the spec comment says "Full Access (Transfer, Share, Delete)", butsharing-service.tsonly ever tests{ $in: ['edit','full'] }, so完全访问behaves exactly like编辑. Worth its own issue; implementing delete/transfer/share semantics is a feature decision, not a bug fix.🤖 Generated with Claude Code