Repository navigation
fix(platform-objects): hide the sys_user 2FA display when twoFactor is off - #25387
Conversation
Claude-Session: https://claude.ai/code/session_01Ukg7cK1DZiCfUdo5FH4G7A Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ukg7cK1DZiCfUdo5FH4G7A Co-authored-by: Claude <noreply@anthropic.com>
Drop two_factor_enabled from the highlights chip and the all_users / me list columns, retire the two_factor list view, and gate the Security tab's 2FA block (now also showing the state) on features.twoFactor. Regenerate the platform-objects translation bundles. Claude-Session: https://claude.ai/code/session_01Ukg7cK1DZiCfUdo5FH4G7A Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 27 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 6a89949c0e34583666422a6aae6efd81b5268f4d && git checkout 6a89949c0e34583666422a6aae6efd81b5268f4d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 97aa34e1f48b213efe40854e1dd426f045c3bfc6 2a544030a714bfa3cb1e8998a384fbf6adcc412a && git checkout -B drift-repro 97aa34e1f48b213efe40854e1dd426f045c3bfc6 && git merge --no-ff 2a544030a714bfa3cb1e8998a384fbf6adcc412a
node scripts/docs-audit/affected-docs.mjs --json 97aa34e1f48b213efe40854e1dd426f045c3bfc6
|
席位核对ACCEPT ·
Generated by Claude Code |
Requested by Tesla · project thread
Fixes #25287
Clause-②: no
Metadata-protocol: none
Unblocks objectstack-ai/cloud#3160 (picks this up with a pin bump). Shape 1 per triage's retriage answer 6357013838: no new authorable key; shape 2 (
requiresFeatureon display surfaces) is not taken.What changes
2FA is either offered, or not shown where it is not wired.
plugin-authresolvestwoFactortofalseunless a deployment turns it on, so on the default deployment the four surfaces below showed a 2FA state nobody could act on.two_factor_enabled("Two-Factor Enabled: No")record:highlightsshowingtwo_factor_enabled, the 2FA quick actions and the closing divider each carry one componentvisibleWhenall_users/melist columnstwo_factor_enabledcolumnlistViews.two_factor)The gate is
has(features.twoFactor) && features.twoFactor == true, in theP(CEL envelope) form the page's other node gates use. The right half is exactlyfeatureGatePredicate('twoFactor'), the conjunctrequiresFeature: 'twoFactor'lowers onto the object's 2FA actions. Thehas()half keeps the gate closed without faulting while the auth config has not loaded (the console bindsfeaturesto an empty map until then).Where each gate is evaluable (measured at the pinned objectui
0d12e953)visibleWhen: evaluable.SchemaRenderer's node scope spreads the ambient predicate scope, andAppContent/RecordFormPagemountExpressionProviderwith the raw/api/v1/auth/configfeatures, sofeatures.twoFactoris bound. A declared node predicate that faults is withheld, not drawn (objectui#13299, present at the pin), so the card's fail-open caveat does not apply to this surface; thehas()guard avoids the fault anyway.record:highlights.fields,ListView.columnsand thelistViewsmap carry no predicate key;ViewTabSchema.visibleis a static boolean. Gating them would need a new authorable key, so they are removed from the defaults instead.Verification
packages/platform-objects/src/pages/sys-user-two-factor-display.test.ts(7 tests). Off: no list view namestwo_factor_enabledandtwo_factoris gone; the chip strip omits the field; every Security-tab node that names 2FA, plus the closing divider, carries the one gate; the gate evaluatesfalse(not a fault) for{ twoFactor: false }, a map without the key, and{}. On: the gate evaluatestrueand the Security tab carries both the state (two_factor_enabled) and the enable action under it. Evaluated with@objectstack/formula'scelEngine, the engine the package's other predicate pins use.2a544030), removingvisibleWhenfrom the Security tab's 2FA caption node turns the pin red (every Security-tab node that names 2FA … carries the one gate: 1 failed, 6 passed); restored withgit checkout HEAD --, blob hash identical to HEAD, 7/7 green again. One-off, no permanent mutation test.platform-objects: 89 test files, 1325 tests pass (vitest run, at2a544030).pnpm --filter @objectstack/platform-objects typecheckexit 0.node scripts/check-i18n-bundles.mjs --write: exit 0; the only change is the retired view's label in en / zh-CN / ja-JP / es-ES.dispatch-gates.mjs --commands, reconciled with--ran): 66 derived, 64 run with exit 0, 2 NOT MEASURED (exit 3, prerequisite refused):check-plugin-teardown-shape.mjs --self-test(its pinned positive-control commit is outside this shallow clone) andcheck:dual-build-cjs-loads(needs every package'sdist/, i.e. a fullpnpm build). Both are left to CI.Acceptance notes
two_factor_enabledfield itself stays: the 2FA actions' predicates read it. A deployment with 2FA on that wants the column or the "2FA Enabled" tab back can declare them in its own list view.plugin-auth/src/auth-plugin.tshas a comment namingsys_user.listViews.{all_users,unverified,two_factor}as an example of schema-embedded list views; it now names one retired view. Left alone to keep this PR inside its claimed file surface.object-collapsed-sections-echo-decisions.test.tscites the retired view's translated label as precedent in a decision string (prose, not an assertion); the precedent stays valid as history.616df659, not re-measured here), reported for the seat to file in objectui: withtwoFactoron,enable_two_factor'svisiblepredicate faults in therecord_sectionscope ofrecord:quick_actions(featuresnot bound there), so the enable button never renders; and objectui's login page has no second-factor step, so a user who enrolled through the API is stuck on/_console/login. Until the first is fixed, the on-case shows the gated heading and state but its action bar stays empty in the real console.🤖 Generated with Claude Code
https://claude.ai/code/session_01Ukg7cK1DZiCfUdo5FH4G7A
Generated by Claude Code