diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md index 52707ccf2..ce3982370 100644 --- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md +++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md @@ -572,6 +572,10 @@ not a replacement status card, CTA band, or page-local nested card. cloud-paid surfaces own Suspend, Activate, Reload, tenant state, and mutation semantics, while `Button` owns the row-action chrome through the secondary `sm` and `xs` sizes. + Security authentication settings actions follow the same boundary: + security/privacy owns auth setup, password-change, credential-rotation, and + read-only semantics, while `Button` owns the warning, primary, secondary, + and settings-action chrome. If a new surface needs a variant that the shared primitive does not expose, extend the primitive and registry guard rather than adding a page-local class string. diff --git a/docs/release-control/v6/internal/subsystems/security-privacy.md b/docs/release-control/v6/internal/subsystems/security-privacy.md index adb257b88..f86af8be2 100644 --- a/docs/release-control/v6/internal/subsystems/security-privacy.md +++ b/docs/release-control/v6/internal/subsystems/security-privacy.md @@ -221,6 +221,11 @@ controls as normal product settings. primitive. Security/privacy owns scope authority, wildcard behavior, preset membership, and custom scope toggles; frontend-primitives owns active and inactive pill tone, focus, disabled treatment, and pressed-state wiring. +6c. Keep authentication setup, password-change, and credential-rotation actions + on the shared `Button` primitive. Security/privacy owns the auth authority, + setup/rotation semantics, and read-only capability state; + frontend-primitives owns warning, primary, secondary, focus, disabled, and + settings-action chrome. 6. Keep the shared storage-directory and secure storage-file hardening helper aligned with the crypto manager plus control-plane magic-link key and store handling whenever runtime data-root ownership assumptions change. 7. Keep auth-env ingestion, hosted commercial base URL validation, and shared fingerprint-verifier TLS defaults aligned whenever runtime auth loading, diff --git a/frontend-modern/scripts/shared-template-registry.json b/frontend-modern/scripts/shared-template-registry.json index 292663312..aaeef0af0 100644 --- a/frontend-modern/scripts/shared-template-registry.json +++ b/frontend-modern/scripts/shared-template-registry.json @@ -970,6 +970,7 @@ { "path": "src/components/Settings/ReportingPanel.tsx" }, { "path": "src/components/Settings/ResourcePicker.tsx" }, { "path": "src/components/Settings/SelfHostedCommercialRecoverySection.tsx" }, + { "path": "src/components/Settings/SecurityAuthPanel.tsx" }, { "path": "src/components/Settings/SSOProvidersPanel.tsx" }, { "path": "src/components/UpdateConfirmationModal.tsx" }, { "path": "src/components/UpdateProgressModal.tsx" }, @@ -1038,6 +1039,14 @@ "min-h-10 sm:min-h-9 px-4 py-2.5 text-sm font-medium rounded-md border border-border text-base-content hover:bg-surface-hover transition-colors disabled:opacity-60 disabled:cursor-not-allowed" ] }, + { + "path": "src/components/Settings/SecurityAuthPanel.tsx", + "patterns": [ + "w-full sm:w-auto px-3 py-2 text-xs font-medium rounded-md border border-amber-300 text-amber-800 bg-amber-100 hover:bg-amber-200 transition-colors dark:border-amber-700 dark:text-amber-200 dark:bg-amber-900 dark:hover:bg-amber-800", + "w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors", + "w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium border border-border text-base-content rounded-md hover:bg-surface-hover transition-colors" + ] + }, { "path": "src/components/Settings/SSOProvidersPanel.tsx", "patterns": [ diff --git a/frontend-modern/src/components/Settings/SecurityAuthPanel.tsx b/frontend-modern/src/components/Settings/SecurityAuthPanel.tsx index b45c68d5f..7552b8aaa 100644 --- a/frontend-modern/src/components/Settings/SecurityAuthPanel.tsx +++ b/frontend-modern/src/components/Settings/SecurityAuthPanel.tsx @@ -1,4 +1,5 @@ import { Component, Show, Accessor, Setter } from 'solid-js'; +import { Button } from '@/components/shared/Button'; import { CalloutCard } from '@/components/shared/CalloutCard'; import SettingsPanel from '@/components/shared/SettingsPanel'; import { Toggle } from '@/components/shared/Toggle'; @@ -75,14 +76,15 @@ export const SecurityAuthPanel: Component = (props) => { {SECURITY_AUTH_DISABLED_READ_ONLY_MESSAGE}

- + @@ -107,26 +109,28 @@ export const SecurityAuthPanel: Component = (props) => {
- - +
User:{' '} diff --git a/frontend-modern/src/components/Settings/__tests__/settingsArchitecture.test.ts b/frontend-modern/src/components/Settings/__tests__/settingsArchitecture.test.ts index 9792ac58f..17aff7a82 100644 --- a/frontend-modern/src/components/Settings/__tests__/settingsArchitecture.test.ts +++ b/frontend-modern/src/components/Settings/__tests__/settingsArchitecture.test.ts @@ -327,6 +327,22 @@ describe('settings architecture guardrails', () => { expect(ssoProvidersPanelSource).not.toContain( 'text-blue-600 hover:underline flex items-center gap-1', ); + + expect(securityAuthPanelSource).toContain( + "import { Button } from '@/components/shared/Button';", + ); + expect(securityAuthPanelSource).toContain('variant="warning"'); + expect(securityAuthPanelSource).toContain('variant="primary"'); + expect(securityAuthPanelSource).toContain('size="settingsAction"'); + expect(securityAuthPanelSource).not.toContain( + 'w-full sm:w-auto px-3 py-2 text-xs font-medium rounded-md border border-amber-300', + ); + expect(securityAuthPanelSource).not.toContain( + 'w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium bg-blue-600', + ); + expect(securityAuthPanelSource).not.toContain( + 'w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium border border-border', + ); }); it('keeps settings callouts on the shared CalloutCard primitive', () => { diff --git a/frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts b/frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts index a874fba54..4427b87d4 100644 --- a/frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts +++ b/frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts @@ -3098,6 +3098,7 @@ describe('shared primitive guardrails', () => { 'src/components/Settings/ReportingPanel.tsx', 'src/components/Settings/ResourcePicker.tsx', 'src/components/Settings/SelfHostedCommercialRecoverySection.tsx', + 'src/components/Settings/SecurityAuthPanel.tsx', 'src/components/Settings/SSOProvidersPanel.tsx', 'src/components/UpdateConfirmationModal.tsx', 'src/components/UpdateProgressModal.tsx', @@ -3167,6 +3168,14 @@ describe('shared primitive guardrails', () => { 'min-h-10 sm:min-h-9 px-4 py-2.5 text-sm font-medium rounded-md border border-border text-base-content hover:bg-surface-hover transition-colors disabled:opacity-60 disabled:cursor-not-allowed', ]), }), + expect.objectContaining({ + path: 'src/components/Settings/SecurityAuthPanel.tsx', + patterns: expect.arrayContaining([ + 'w-full sm:w-auto px-3 py-2 text-xs font-medium rounded-md border border-amber-300 text-amber-800 bg-amber-100 hover:bg-amber-200 transition-colors dark:border-amber-700 dark:text-amber-200 dark:bg-amber-900 dark:hover:bg-amber-800', + 'w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors', + 'w-full sm:w-auto min-h-10 sm:min-h-10 px-4 py-2.5 text-sm font-medium border border-border text-base-content rounded-md hover:bg-surface-hover transition-colors', + ]), + }), expect.objectContaining({ path: 'src/components/Settings/SSOProvidersPanel.tsx', patterns: expect.arrayContaining([ @@ -3955,6 +3964,21 @@ describe('shared primitive guardrails', () => { expect(selfHostedCommercialRecoverySectionSource).not.toContain( 'min-h-10 sm:min-h-9 px-4 py-2.5 text-sm font-medium rounded-md border border-border text-base-content hover:bg-surface-hover transition-colors disabled:opacity-60 disabled:cursor-not-allowed', ); + expect(securityAuthPanelSource).toContain('@/components/shared/Button'); + expect(securityAuthPanelSource).toContain('