Skip to content

[Decision] #16712's position-name refusal reads the position catalog of every organization: scope it to the writer's organization, or keep the ruled literal reading #20297

Description

@objectstack-fleet

On a deployment where organizations are walled off from one another, an assignment naming a position that exists only in another organization is accepted and silently grants nothing. The same accept-or-refuse answer tells any organization admin whether some other organization has a position by that name.

Filed by the domain:services seat (#6021, session_01TEah6PeJGjxJfbHaySJjLQ). This question arose while executing #16712's ruling (5582062659, confirmed 5582244791) on PR #20292, so it gets its own card, linked back to that ruling; #16712 is not re-labelled needs-user-decision. The implementing dev raised it as an open question (5858844438), and the seat re-measured it on the PR head. ⛔ Not a claim.

Background

Governing text

This card changes no protocol file and no packages/spec source under any answer.

Premises, each with its re-check

  1. The PR reads the catalog without a tenant.
    • Re-check: git show <PR head>:packages/plugins/plugin-security/src/position-catalog-refusal.ts | grep -n "^const SYSTEM_CTX" → { isSystem: true } as const.
    • Control: grep -n "export async function namesWithoutCatalogRow" → 1 hit.
  2. The engine's own lookup probe keeps the tenant wall.
    • Re-check: git grep -n "cross-tenant existence" origin/main -- packages/objectql/src/engine.ts → 1 hit.
    • Control: git grep -c "assertReferencesResolve" origin/main -- packages/objectql/src/engine.ts → ≥1.
  3. The platform's own "Assign position" picker can only produce names the writer's organization can see.
    • packages/platform-objects/src/pages/sys-user.page.ts pickers read sys_position with valueField: 'name' under the caller's context.
    • Re-check: git grep -n "valueField: 'name'" origin/main -- packages/platform-objects/src/pages/sys-user.page.ts → 2 hits (:176 comment, :191 the picker).
  4. No non-test writer stores a name this refusal could newly refuse.
    • The in-repo census on 4d7e740d3b was EMPTY (dev report 5858844438).
    • The seat re-ran the catalog-less-name leg on e6b7d8c861: git grep -nE "position:\s*['\"](org_member|everyone|org_owner|org_admin|authenticated|guest|anonymous|platform_admin)['\"]" -- 'packages/**/*.ts' 'examples/**/*.ts' ':!**/*.test.ts' → 0 hits. Control, the same pattern in **/*.test.ts → 3 hits.
    • Out-of-repo: hotcrm and hotclm are EMPTY (5857658468).

Question

On a walled deployment, a writer in organization A stores an assignment whose position name exists only in organization B's catalog. Does the platform accept it (201) or refuse it (400)?

Options

option what happens what a customer sees
A: keep the literal reading (as PR #20292 stands) The catalog is read across every organization; a name any organization carries is accepted. The assignment saves and grants nothing, silently. Organization A's admin can tell "some other organization has this position" (201) from "no one has it" (400), measured in the PR's walled test.
B: the writer's organization plus organization-less rows The read takes the engine lookup probe's spelling, { ...context, isSystem: true }. A name only another organization carries is refused like a name that exists nowhere, with the same envelope and the same message. A writer with no organization in context (a platform-level caller) still reads every organization, as the engine does. A loud 400 that names the fix; the two cases answer identically. The dev estimates one line in namesWithoutCatalogRow plus reversing one pin.
C: the assignment row's own organization_id plus organization-less rows The check matches exactly where the resolver will look. Same as B for an organization admin. A platform admin writing for organization B is judged against B's catalog. A row with no organization_id needs its own rule, which nobody has asked for.
  • A in business terms: an HR system that lets you assign someone the job title "Plant Manager" because some other customer on the same server has that title. The assignment does nothing, and the answer tells you the other customer exists.
  • B in business terms: a job title must exist in your company's title list. This is how Salesforce roles and Workday job profiles behave per tenant.
  • C in business terms: the same as B, except that the platform operator acting on a customer's behalf is checked against that customer's list.

四维分析(业务立场)

Prior rulings read: cross-tenant existence / existence oracle|existence probe → 1 + 1 hits; ADR-0120 (the oracle class), ADR-0131 D3/D13/D14 (end state); thread: #16712 5582062659, 5582244791, 5857658468, 5858844438; #19808; #19819; #19860.

推荐 B(回退项:A,仅当维护者认定裁决字面优先且接受该探测口;⛔ C 不单独推荐)。只看①选 B;②③④ 是否翻转:否。
置信缺口:

  • 本席未在真实隔离部署(PG + org-scoping 插件)上端到端测过。walled 读数来自 dev 在引擎层、sqlite 上的实测。
  • "消费方都是单组织形态"取自 dev 报告,本席未复测。
  • 未读 objectui"分配岗位"对话框在平台管理员跨组织操作时读的是哪个组织的目录。

Execution per answer

Related: #16712 · PR #20292 · #19808 / PR #19836 · #19819 / PR #19859 · #19860 / PR #19866 · #15196 · #17247 · ADR-0131 · ADR-0120.

Dedupe: GitHub semantic issue search in this repository, closed included:

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:servicespriority:p1High: required for production / M2security

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions