Skip to content

sys_organization's platform-owned columns are engine-writable but no product surface reaches them — the data door answers 405 (apiMethods: ['get', 'list']) and better-auth's update endpoint does not carry them #15873

Description

@zhuangjianguo

Found while landing #14238's sys_organization.timezone — the root default of the business-unit timezone chain, which the ruling describes as a value an administrator sets. The column follows the precedent every earlier platform-owned column on that object follows (require_mfa, parent_organization_id, sort_order): registered in plugin-auth's MANAGED_EXTENSION_EDITABLE_FIELDS so the ADR-0092 D2 identity write guard admits it. This card is about where that precedent leads. Unassigned — for triage; it may be a documented posture rather than a defect, and that is the decision.

The reading

Measured on origin/main at 7b6825477:

  • packages/platform-objects/src/identity/sys-organization.object.tsenable.apiMethods: ['get', 'list'] (its own comment: managedBy is not enforced: generic CRUD bypasses better-auth on sys_team (data-integrity / security) #1591, reads only, writes refused by the identity write guard and owned by better-auth; "HTTP answers 405 before the 403").
  • packages/rest/src/rest-server.ts:1699 — a non-empty enable.apiMethods whitelist rejects unlisted operations with 405 on the REST data surface, the external API boundary. So PATCH /api/v1/data/sys_organization/ID never reaches the engine.
  • packages/plugins/plugin-auth/src/managed-extension-fields.tsMANAGED_EXTENSION_EDITABLE_FIELDS.sys_organization = require_mfa, parent_organization_id, sort_order (and timezone once No platform object carries a timezone, so every app that computes a date boundary has to invent one — and each will invent it differently #14238's PR lands), registered at kernel:ready as the guard's per-object update whitelist (auth-plugin.ts, the D7 loop). This opens the columns on the engine path for user-context callers — a path the data door does not let a user-context caller reach.
  • The object's update_organization row action targets better-auth's organization/update (bodyShape: { wrap: 'data' }) with params name, slug, logo only; the extension fields are deliberately not better-auth additionalFields (the ai_access / locale notes in auth-manager.ts), so that endpoint does not carry them either.
  • Writers of require_mfa outside plugin-auth's own read site (auth-manager.ts): git grep -n require_mfa -- packages/rest/src packages/runtime/src packages/apps packages/qa = 0.

So the four columns are settable only by a system-context caller — a plugin, a flow, a CLI seed, SQL. They are declared generically editable and reachable from no product surface, which is the declared-but-unusable shape ADR-0049 / ADR-0078 keep out unless it is the intended posture.

Not measured

Whether the Setup app (objectui side) renders an organization edit form for these columns at all; if it does, it meets the 405 above. Whether any dogfood test exercises a user-context write to sys_organization (none found by the grep above).

Fix space (a decision, not a prescription)

  • (a) admit update in sys_organization.apiMethods and let the D2 whitelist do the column gating — the whitelist already exists for exactly this, and better-auth's own columns stay stripped;
  • (b) a dedicated admin route for the platform-owned columns;
  • (c) declare the posture: the columns are system-writable only, and drop them from MANAGED_EXTENSION_EDITABLE_FIELDS so the declaration matches the reach.

The #14238 seat did not touch apiMethods: the method gate on an identity table is an ADR-0092 / #1591 decision, outside ruling A's scope.

Published surface (measured)

sys_organization's apiMethods is in the built packages/platform-objects/dist/identity/index.js / index.mjs; content/docs/** was not measured for this observation.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions