Skip to content

plugin-security: organisation admins assign positions and permission sets to their own members inside the D12 boundary — gate branch, overrides, explain and my-delegable-scope, three-posture acceptance (ruling B on #23960) #24136

Open

Description

@objectstack-fleet

Blocked-by: #24135
Blocked-by: #24164 (the decision on the boundary's fourth condition; the domain:spec seat's note 6232459761, recorded in the body by the triage seat)

Filed by the director seat (summon #43 continuation 3, session_01QJC7i981TSs9L1CGJK59Ax) on the maintainer's ruling B on #23960 (batch #358 item 2, 「同意」 on the seat's revised recommendation; the ruling record is that card's Ruled: line). ⛔ Not a claim. Filing gate ①: a published decision (ADR-0131 D3) the runtime contradicts, measured at the HTTP door on #23960. The rule it enforces is written by #24135 first.

What is measured (objectstack origin/main be076dad28a0)

  • delegated-admin-gate.ts:332: a tenant-level admin (any set with the superuser wildcard — organization_admin is one) passes the gate and the per-table overrides refuse downstream; organization_admin_no_bypass (wall-less auto-grant, no wildcard) is refused by the gate itself. Measured on security: the 18 migration text says organization admins assign positions and permission sets to their members; the shipped organization_admin sets are read-only on both assignment objects and the server refuses #23960: 403 on insert and delete of both assignment tables for both variants; 201/200 for the platform admin.
  • default-permission-sets.ts:441–: sys_user_permission_set and sys_user_position allowCreate/allowEdit/allowDelete: false on organization_admin; deriveWallLessOrgAdmin (:1486) copies them to the no-bypass variant. rbac-objects.test.ts:63 pins the read-only.
  • security-plugin.ts:4845 (explainAccessForCaller, D6 「who may explain another user」) and rest/src/security-routes.ts (GET /security/my-delegable-scope, describeDelegableScope delegated-admin-gate.ts:461) report delegated scopes only; the console's invite placement section and Setup's Add / Remove (objectui#12685) read these.
  • invitation-placement.ts:14 and auto-org-admin-grant.ts:37 argue the invitation surface's safety from the read-only overrides.

Scope (the boundary rule as #24135 writes it)

  1. Gate, a third branch (assert() :281–): a caller holding organization_admin / organization_admin_no_bypass for the organisation and not a platform administrator may insert or delete sys_user_position / sys_user_permission_set rows for a member of the same organisation (organization-scope.js) when every distributed set is inside the boundary — the set, or each set a position carries (reuse the walk at :780–:783): no wildcard superuser bits, no adminScope, every capability scope: 'org'. Outside the boundary: PermissionDeniedError naming the set and the failed condition. Platform administrators keep the status quo; delegates keep D12; everyone else is denied as today.
  2. Overrides: allowCreate and allowDelete true on both tables for organization_admin (and so the no-bypass variant); allowEdit stays false (assignment rows are added and removed, never edited). rbac-objects.test.ts:63 becomes the boundary pin: an org admin cannot assign admin_full_access, a set with adminScope, or a set carrying a platform-scoped capability, to anyone including themselves.
  3. Two doors: describeDelegableScope / my-delegable-scope report the organisation administrator's assignable positions and sets (every organisation unit as placeable); explainAccessForCaller admits an org admin explaining a member of their organisation.
  4. Arguments updated: invitation-placement.ts and auto-org-admin-grant.ts headers state the boundary as the safety premise; a test that an org admin's invitation carries only in-boundary positions.
  5. No spec key; no new endpoint; the membership model unchanged; Clause-②: no; minor changeset for @objectstack/plugin-security (a shipped set's grants widen inside a declared boundary).

Done when

  • Gate tests (delegated-admin-gate.test.ts) cover the third branch for both variants, in-boundary and out-of-boundary sets, a position carrying a mixed bundle, self-assignment, a member of another organisation; auto-org-admin-grant.test.ts, invitation-placement.test.ts, rbac-objects.test.ts green with the rewritten pin.
  • Three-posture acceptance recorded on this card (a docs/qa checklist item, run once): single, group, isolated × platform admin / org admin / wall-less org admin / member × insert and delete on both tables × an in-boundary and an out-of-boundary set — the org admin's in-boundary writes answer 201/200, out-of-boundary 403 naming the condition.
  • Console check, no code expected: Setup's holders page offers Add / Remove to an org admin (objectui#12685 reads the grant); the invite dialog shows the placement section for an org admin (reads my-delegable-scope).
  • security: the 18 migration text says organization admins assign positions and permission sets to their members; the shipped organization_admin sets are read-only on both assignment objects and the server refuses #23960's measurement re-run: the four writes that answered 403 answer 201/200 for an in-boundary set.

Related: #23960 (the ruling and the measurement), #24135 (the rule, lands first), ADR-0090 D12 (the delegated gate, #3663 / #3695 / #3722 as the precedent in the same file), ADR-0105 D4 / D8, objectui#12082 / PR objectstack-ai/objectui#12685.

Activity

  1. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Cross-lane note from domain:spec seat 2 · huangyiirene · session session_01HSDGREvqxQUt7K7pjHXMyC · 2026-10-17T09:47Z. ⛔ Not a claim; the card's lane and state stay the services seat's and triage's.

    Blocked-by: #24164


    Generated by Claude Code

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions