@actuate-media/cms-admin 0.27.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/AdminRoot.d.ts +7 -0
  2. package/dist/AdminRoot.d.ts.map +1 -1
  3. package/dist/AdminRoot.js +7 -2
  4. package/dist/AdminRoot.js.map +1 -1
  5. package/dist/__tests__/views/account-security.render.test.d.ts +2 -0
  6. package/dist/__tests__/views/account-security.render.test.d.ts.map +1 -0
  7. package/dist/__tests__/views/account-security.render.test.js +98 -0
  8. package/dist/__tests__/views/account-security.render.test.js.map +1 -0
  9. package/dist/__tests__/views/login-mfa.render.test.d.ts +2 -0
  10. package/dist/__tests__/views/login-mfa.render.test.d.ts.map +1 -0
  11. package/dist/__tests__/views/login-mfa.render.test.js +73 -0
  12. package/dist/__tests__/views/login-mfa.render.test.js.map +1 -0
  13. package/dist/__tests__/views/security-settings.render.test.d.ts +2 -0
  14. package/dist/__tests__/views/security-settings.render.test.d.ts.map +1 -0
  15. package/dist/__tests__/views/security-settings.render.test.js +175 -0
  16. package/dist/__tests__/views/security-settings.render.test.js.map +1 -0
  17. package/dist/actuate-admin.css +1 -1
  18. package/dist/components/Breadcrumbs.d.ts.map +1 -1
  19. package/dist/components/Breadcrumbs.js +1 -0
  20. package/dist/components/Breadcrumbs.js.map +1 -1
  21. package/dist/views/Login.d.ts +16 -1
  22. package/dist/views/Login.d.ts.map +1 -1
  23. package/dist/views/Login.js +55 -3
  24. package/dist/views/Login.js.map +1 -1
  25. package/dist/views/Profile.d.ts +12 -0
  26. package/dist/views/Profile.d.ts.map +1 -0
  27. package/dist/views/Profile.js +16 -0
  28. package/dist/views/Profile.js.map +1 -0
  29. package/dist/views/Settings.d.ts.map +1 -1
  30. package/dist/views/Settings.js +3 -11
  31. package/dist/views/Settings.js.map +1 -1
  32. package/dist/views/profile/AccountSecurity.d.ts +2 -0
  33. package/dist/views/profile/AccountSecurity.d.ts.map +1 -0
  34. package/dist/views/profile/AccountSecurity.js +117 -0
  35. package/dist/views/profile/AccountSecurity.js.map +1 -0
  36. package/dist/views/profile/useMfaEnrollment.d.ts +35 -0
  37. package/dist/views/profile/useMfaEnrollment.d.ts.map +1 -0
  38. package/dist/views/profile/useMfaEnrollment.js +103 -0
  39. package/dist/views/profile/useMfaEnrollment.js.map +1 -0
  40. package/dist/views/settings/AccessProtectionCard.d.ts +10 -0
  41. package/dist/views/settings/AccessProtectionCard.d.ts.map +1 -0
  42. package/dist/views/settings/AccessProtectionCard.js +20 -0
  43. package/dist/views/settings/AccessProtectionCard.js.map +1 -0
  44. package/dist/views/settings/AuthMfaCard.d.ts +11 -0
  45. package/dist/views/settings/AuthMfaCard.d.ts.map +1 -0
  46. package/dist/views/settings/AuthMfaCard.js +34 -0
  47. package/dist/views/settings/AuthMfaCard.js.map +1 -0
  48. package/dist/views/settings/SecretsAuditCard.d.ts +11 -0
  49. package/dist/views/settings/SecretsAuditCard.d.ts.map +1 -0
  50. package/dist/views/settings/SecretsAuditCard.js +22 -0
  51. package/dist/views/settings/SecretsAuditCard.js.map +1 -0
  52. package/dist/views/settings/SecurityStatusCard.d.ts +6 -0
  53. package/dist/views/settings/SecurityStatusCard.d.ts.map +1 -0
  54. package/dist/views/settings/SecurityStatusCard.js +35 -0
  55. package/dist/views/settings/SecurityStatusCard.js.map +1 -0
  56. package/dist/views/settings/SecurityTab.d.ts +5 -0
  57. package/dist/views/settings/SecurityTab.d.ts.map +1 -0
  58. package/dist/views/settings/SecurityTab.js +41 -0
  59. package/dist/views/settings/SecurityTab.js.map +1 -0
  60. package/dist/views/settings/SessionPolicyCard.d.ts +11 -0
  61. package/dist/views/settings/SessionPolicyCard.d.ts.map +1 -0
  62. package/dist/views/settings/SessionPolicyCard.js +21 -0
  63. package/dist/views/settings/SessionPolicyCard.js.map +1 -0
  64. package/dist/views/settings/securityPrimitives.d.ts +21 -0
  65. package/dist/views/settings/securityPrimitives.d.ts.map +1 -0
  66. package/dist/views/settings/securityPrimitives.js +31 -0
  67. package/dist/views/settings/securityPrimitives.js.map +1 -0
  68. package/dist/views/settings/useSecuritySettings.d.ts +37 -0
  69. package/dist/views/settings/useSecuritySettings.d.ts.map +1 -0
  70. package/dist/views/settings/useSecuritySettings.js +154 -0
  71. package/dist/views/settings/useSecuritySettings.js.map +1 -0
  72. package/package.json +3 -2
  73. package/src/AdminRoot.tsx +18 -0
  74. package/src/__tests__/views/account-security.render.test.tsx +140 -0
  75. package/src/__tests__/views/login-mfa.render.test.tsx +88 -0
  76. package/src/__tests__/views/security-settings.render.test.tsx +212 -0
  77. package/src/components/Breadcrumbs.tsx +1 -0
  78. package/src/views/Login.tsx +247 -94
  79. package/src/views/Profile.tsx +54 -0
  80. package/src/views/Settings.tsx +3 -33
  81. package/src/views/profile/AccountSecurity.tsx +406 -0
  82. package/src/views/profile/useMfaEnrollment.ts +120 -0
  83. package/src/views/settings/AccessProtectionCard.tsx +143 -0
  84. package/src/views/settings/AuthMfaCard.tsx +133 -0
  85. package/src/views/settings/SecretsAuditCard.tsx +129 -0
  86. package/src/views/settings/SecurityStatusCard.tsx +110 -0
  87. package/src/views/settings/SecurityTab.tsx +163 -0
  88. package/src/views/settings/SessionPolicyCard.tsx +123 -0
  89. package/src/views/settings/securityPrimitives.tsx +75 -0
  90. package/src/views/settings/useSecuritySettings.ts +197 -0
@@ -0,0 +1,103 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { cmsApi } from '../../lib/api.js';
4
+ /**
5
+ * Drives the per-user MFA (TOTP) enrollment loop against the real auth API:
6
+ * 1. `beginSetup(password)` — password reauth → POST /auth/totp/setup, which
7
+ * returns the provisioning URI + one-time backup codes.
8
+ * 2. `confirmSetup(code)` — POST /auth/totp/verify to flip totpEnabled on.
9
+ * 3. `disable(password)` — password reauth → POST /auth/totp/disable.
10
+ *
11
+ * The hook never holds the user's password and never persists the secret or
12
+ * backup codes beyond the in-memory enrollment object (cleared on cancel/finish).
13
+ */
14
+ export function useMfaEnrollment() {
15
+ const [status, setStatus] = useState('loading');
16
+ const [enrollment, setEnrollment] = useState(null);
17
+ const [busy, setBusy] = useState(false);
18
+ const refresh = useCallback(async () => {
19
+ const res = await cmsApi('/auth/me');
20
+ if (res.error || !res.data) {
21
+ setStatus('error');
22
+ return;
23
+ }
24
+ setStatus(res.data.totpEnabled ? 'enabled' : 'disabled');
25
+ }, []);
26
+ useEffect(() => {
27
+ void refresh();
28
+ }, [refresh]);
29
+ const beginSetup = useCallback(async (password) => {
30
+ if (!password)
31
+ return { ok: false, error: 'Enter your current password to continue.' };
32
+ setBusy(true);
33
+ try {
34
+ const res = await cmsApi('/auth/totp/setup', {
35
+ method: 'POST',
36
+ headers: { 'x-reauth-password': password },
37
+ body: JSON.stringify({}),
38
+ });
39
+ if (res.error || !res.data) {
40
+ return { ok: false, error: res.error ?? 'Could not start MFA setup.' };
41
+ }
42
+ setEnrollment(res.data);
43
+ return { ok: true };
44
+ }
45
+ finally {
46
+ setBusy(false);
47
+ }
48
+ }, []);
49
+ const confirmSetup = useCallback(async (code) => {
50
+ const trimmed = code.trim();
51
+ if (!/^\d{6}$/.test(trimmed)) {
52
+ return { ok: false, error: 'Enter the 6-digit code from your authenticator app.' };
53
+ }
54
+ setBusy(true);
55
+ try {
56
+ const res = await cmsApi('/auth/totp/verify', {
57
+ method: 'POST',
58
+ body: JSON.stringify({ code: trimmed }),
59
+ });
60
+ if (res.error)
61
+ return { ok: false, error: res.error };
62
+ setEnrollment(null);
63
+ setStatus('enabled');
64
+ return { ok: true };
65
+ }
66
+ finally {
67
+ setBusy(false);
68
+ }
69
+ }, []);
70
+ const cancelSetup = useCallback(() => {
71
+ setEnrollment(null);
72
+ }, []);
73
+ const disable = useCallback(async (password) => {
74
+ if (!password)
75
+ return { ok: false, error: 'Enter your current password to continue.' };
76
+ setBusy(true);
77
+ try {
78
+ const res = await cmsApi('/auth/totp/disable', {
79
+ method: 'POST',
80
+ headers: { 'x-reauth-password': password },
81
+ body: JSON.stringify({}),
82
+ });
83
+ if (res.error)
84
+ return { ok: false, error: res.error };
85
+ setStatus('disabled');
86
+ return { ok: true };
87
+ }
88
+ finally {
89
+ setBusy(false);
90
+ }
91
+ }, []);
92
+ return {
93
+ status,
94
+ enrollment,
95
+ busy,
96
+ refresh,
97
+ beginSetup,
98
+ confirmSetup,
99
+ cancelSetup,
100
+ disable,
101
+ };
102
+ }
103
+ //# sourceMappingURL=useMfaEnrollment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMfaEnrollment.js","sourceRoot":"","sources":["../../../src/views/profile/useMfaEnrollment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type IpAllowlistStatus, type SecretsStatus, type SecuritySourceMetadata } from '@actuate-media/cms-core/security-center';
2
+ export declare function AccessProtectionCard({ ipAllowlist, secrets, csrfEnabled, uploadValidation, sources, onNavigate, }: {
3
+ ipAllowlist: IpAllowlistStatus;
4
+ secrets: SecretsStatus;
5
+ csrfEnabled: boolean;
6
+ uploadValidation: boolean;
7
+ sources: Record<string, SecuritySourceMetadata>;
8
+ onNavigate?: (path: string) => void;
9
+ }): import("react/jsx-runtime").JSX.Element;
10
+ //# sourceMappingURL=AccessProtectionCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccessProtectionCard.d.ts","sourceRoot":"","sources":["../../../src/views/settings/AccessProtectionCard.tsx"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC5B,MAAM,yCAAyC,CAAA;AAIhD,wBAAgB,oBAAoB,CAAC,EACnC,WAAW,EACX,OAAO,EACP,WAAW,EACX,gBAAgB,EAChB,OAAO,EACP,UAAU,GACX,EAAE;IACD,WAAW,EAAE,iBAAiB,CAAA;IAC9B,OAAO,EAAE,aAAa,CAAA;IACtB,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,OAAO,CAAA;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAA;IAC/C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,2CAmHA"}
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId, useState } from 'react';
4
+ import { Check, X } from 'lucide-react';
5
+ import { validateIpRange, } from '@actuate-media/cms-core/security-center';
6
+ import { SettingsSourceBadge } from './components.js';
7
+ import { SecurityCard, StatusRow } from './securityPrimitives.js';
8
+ export function AccessProtectionCard({ ipAllowlist, secrets, csrfEnabled, uploadValidation, sources, onNavigate, }) {
9
+ const headingId = useId();
10
+ const ipTestId = useId();
11
+ const [ipTest, setIpTest] = useState('');
12
+ const ipTestValid = ipTest.trim() === '' ? null : validateIpRange(ipTest);
13
+ const distributed = secrets.rateLimitBackend === 'distributed';
14
+ return (_jsxs(SecurityCard, { headingId: headingId, title: "Access & Request Protection", description: "Network-level access restrictions and request-level protections. These are enforced by the platform; edit network rules in config.", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { className: "text-foreground text-sm font-medium", children: "Admin IP allowlist" }), _jsx(SettingsSourceBadge, { meta: sources.ipAllowlist })] }), _jsx(StatusRow, { status: ipAllowlist.enabled ? 'pass' : 'unknown', label: "Status", value: ipAllowlist.enabled ? `${ipAllowlist.ranges.length} range(s)` : 'Not configured' }), ipAllowlist.currentIp && (_jsx(StatusRow, { status: ipAllowlist.currentIpAllowed === null
15
+ ? 'unknown'
16
+ : ipAllowlist.currentIpAllowed
17
+ ? 'pass'
18
+ : 'fail', label: "Your current IP", value: _jsxs("span", { className: "font-mono text-xs", children: [ipAllowlist.currentIp, ipAllowlist.currentIpAllowed === false && ' (blocked)'] }) })), ipAllowlist.enabled && (_jsx("div", { className: "text-muted-foreground flex flex-wrap gap-1.5 pt-1", children: ipAllowlist.ranges.map((r) => (_jsx("code", { className: "bg-muted rounded px-1.5 py-0.5 font-mono text-xs", children: r }, r))) })), _jsxs("div", { className: "pt-1", children: [_jsx("label", { htmlFor: ipTestId, className: "text-muted-foreground mb-1 block text-sm", children: "Validate an IP or CIDR range" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { id: ipTestId, type: "text", value: ipTest, onChange: (e) => setIpTest(e.target.value), placeholder: "e.g. 203.0.113.0/24", className: "border-border bg-input-background text-foreground focus-visible:ring-ring w-full max-w-xs rounded-md border px-3 py-1.5 font-mono text-sm focus-visible:ring-2 focus-visible:outline-none" }), ipTestValid === true && (_jsxs("span", { className: "text-success flex items-center gap-1 text-sm", children: [_jsx(Check, { size: 15, "aria-hidden": "true" }), " Valid"] })), ipTestValid === false && (_jsxs("span", { className: "text-destructive flex items-center gap-1 text-sm", children: [_jsx(X, { size: 15, "aria-hidden": "true" }), " Invalid"] }))] }), _jsxs("p", { className: "text-muted-foreground mt-1 text-xs", children: ["Add validated ranges to ", _jsx("code", { className: "font-mono", children: "admin.ipAllowlist" }), " in actuate.config.ts; the middleware enforces them."] })] })] }), _jsxs("div", { className: "border-border mt-4 space-y-2 border-t pt-4", children: [_jsx(StatusRow, { status: csrfEnabled ? 'pass' : 'fail', label: "CSRF protection", value: csrfEnabled ? 'Enabled' : 'Disabled', badge: _jsx(SettingsSourceBadge, { meta: sources.csrf }) }), _jsx(StatusRow, { status: distributed ? 'pass' : 'warning', label: "Rate limiting", value: distributed ? 'Distributed (Upstash)' : 'In-memory', badge: _jsx(SettingsSourceBadge, { meta: sources.rateLimiting }) }), _jsx(StatusRow, { status: uploadValidation ? 'pass' : 'warning', label: "Upload validation", value: "MIME + magic bytes + SVG sanitization", badge: _jsx(SettingsSourceBadge, { meta: sources.uploadValidation }) }), onNavigate && (_jsx("button", { type: "button", onClick: () => onNavigate('/media'), className: "text-brand mt-1 text-sm hover:underline", children: "Manage upload rules in Media \u2192" }))] })] }));
19
+ }
20
+ //# sourceMappingURL=AccessProtectionCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccessProtectionCard.js","sourceRoot":"","sources":["../../../src/views/settings/AccessProtectionCard.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { SecurityForm } from './useSecuritySettings.js';
2
+ export declare function AuthMfaCard({ form, setField, canEdit, viewerHasMfa, withMfa, totalAdmins, onNavigate, }: {
3
+ form: SecurityForm;
4
+ setField: <K extends keyof SecurityForm>(key: K, value: SecurityForm[K]) => void;
5
+ canEdit: boolean;
6
+ viewerHasMfa: boolean;
7
+ withMfa: number;
8
+ totalAdmins: number;
9
+ onNavigate?: (path: string) => void;
10
+ }): import("react/jsx-runtime").JSX.Element;
11
+ //# sourceMappingURL=AuthMfaCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthMfaCard.d.ts","sourceRoot":"","sources":["../../../src/views/settings/AuthMfaCard.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAQ5D,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,OAAO,EACP,WAAW,EACX,UAAU,GACX,EAAE;IACD,IAAI,EAAE,YAAY,CAAA;IAClB,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,YAAY,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAChF,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,2CAqGA"}
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId, useState } from 'react';
4
+ import { ShieldCheck } from 'lucide-react';
5
+ import { MFA_GRACE_PERIOD_OPTIONS } from '@actuate-media/cms-core/security-center';
6
+ import { ConfirmDangerousSettingDialog, SettingsToggleRow } from './components.js';
7
+ import { SecurityCard, StatusRow } from './securityPrimitives.js';
8
+ function graceLabel(days) {
9
+ if (days === 0)
10
+ return 'Immediately';
11
+ if (days === 1)
12
+ return '1 day';
13
+ return `${days} days`;
14
+ }
15
+ export function AuthMfaCard({ form, setField, canEdit, viewerHasMfa, withMfa, totalAdmins, onNavigate, }) {
16
+ const headingId = useId();
17
+ const graceId = useId();
18
+ const [confirmEnable, setConfirmEnable] = useState(false);
19
+ function requestMfaToggle(value) {
20
+ if (value && !form.mfaRequired) {
21
+ setConfirmEnable(true);
22
+ return;
23
+ }
24
+ setField('mfaRequired', value);
25
+ }
26
+ // Lockout-safe: the toggle can't be turned on until the acting admin has
27
+ // their own MFA configured (the server enforces this too).
28
+ const mfaToggleDisabled = !canEdit || (!form.mfaRequired && !viewerHasMfa);
29
+ return (_jsxs(SecurityCard, { headingId: headingId, title: "Authentication & MFA", description: "Require multi-factor authentication for admin accounts and control the enrollment window.", children: [_jsxs("div", { className: "space-y-4", children: [_jsx(SettingsToggleRow, { label: "Require MFA for admin users", description: "Admins must set up a TOTP authenticator app. Existing admins are prompted to enroll within the grace period.", checked: form.mfaRequired, disabled: mfaToggleDisabled, onChange: requestMfaToggle }), _jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("label", { htmlFor: graceId, className: "flex-1", children: [_jsx("span", { className: "text-foreground text-base font-medium", children: "Enrollment grace period" }), _jsx("p", { className: "text-muted-foreground mt-0.5 text-sm", children: "How long existing admins have to set up MFA before it is enforced." })] }), _jsx("select", { id: graceId, value: form.mfaGracePeriodDays, disabled: !canEdit || !form.mfaRequired, onChange: (e) => setField('mfaGracePeriodDays', Number(e.target.value)), className: "border-border bg-input-background text-foreground focus-visible:ring-ring rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50", children: MFA_GRACE_PERIOD_OPTIONS.map((d) => (_jsx("option", { value: d, children: graceLabel(d) }, d))) })] })] }), _jsxs("div", { className: "border-border mt-4 space-y-2 border-t pt-4", children: [_jsx(StatusRow, { status: viewerHasMfa ? 'pass' : 'warning', label: "Your account", value: viewerHasMfa ? 'MFA enabled' : 'MFA not set up' }), !viewerHasMfa && (_jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-muted-foreground text-sm", children: "Set up MFA on your own account before requiring it for everyone, or you may lock yourself out." }), onNavigate && (_jsxs("button", { type: "button", onClick: () => onNavigate('/profile'), className: "border-border text-foreground hover:bg-accent inline-flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm transition-colors", children: [_jsx(ShieldCheck, { size: 15, "aria-hidden": "true" }), "Set up MFA"] }))] })), _jsx(StatusRow, { status: totalAdmins === 0 ? 'unknown' : withMfa === totalAdmins ? 'pass' : 'warning', label: "Admin MFA coverage", value: totalAdmins === 0 ? 'No admins' : `${withMfa} of ${totalAdmins} admins` })] }), _jsx(ConfirmDangerousSettingDialog, { open: confirmEnable, title: "Require MFA for all admin users?", description: `Admins without MFA will be prompted to enroll and must complete setup within the grace period (${graceLabel(form.mfaGracePeriodDays)}). Make sure your own account has MFA configured first.`, confirmLabel: "Require MFA", onConfirm: () => {
30
+ setField('mfaRequired', true);
31
+ setConfirmEnable(false);
32
+ }, onCancel: () => setConfirmEnable(false) })] }));
33
+ }
34
+ //# sourceMappingURL=AuthMfaCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthMfaCard.js","sourceRoot":"","sources":["../../../src/views/settings/AuthMfaCard.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ApiKeysSecuritySummary, SecretsStatus, SecuritySourceMetadata, UsersSecuritySummary } from '@actuate-media/cms-core/security-center';
2
+ export declare function SecretsAuditCard({ secrets, auditEnabled, failedLogins24h, users, apiKeys, sources, onNavigate, }: {
3
+ secrets: SecretsStatus;
4
+ auditEnabled: boolean;
5
+ failedLogins24h: number;
6
+ users: UsersSecuritySummary;
7
+ apiKeys: ApiKeysSecuritySummary;
8
+ sources: Record<string, SecuritySourceMetadata>;
9
+ onNavigate?: (path: string) => void;
10
+ }): import("react/jsx-runtime").JSX.Element;
11
+ //# sourceMappingURL=SecretsAuditCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecretsAuditCard.d.ts","sourceRoot":"","sources":["../../../src/views/settings/SecretsAuditCard.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,sBAAsB,EAEtB,aAAa,EACb,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,yCAAyC,CAAA;AAiBhD,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,YAAY,EACZ,eAAe,EACf,KAAK,EACL,OAAO,EACP,OAAO,EACP,UAAU,GACX,EAAE;IACD,OAAO,EAAE,aAAa,CAAA;IACtB,YAAY,EAAE,OAAO,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,KAAK,EAAE,oBAAoB,CAAA;IAC3B,OAAO,EAAE,sBAAsB,CAAA;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAA;IAC/C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,2CAsFA"}
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId } from 'react';
4
+ import { SettingsSourceBadge } from './components.js';
5
+ import { SecurityCard, StatusRow } from './securityPrimitives.js';
6
+ function secretValue(status) {
7
+ switch (status) {
8
+ case 'pass':
9
+ return 'Configured';
10
+ case 'warning':
11
+ return 'Not configured';
12
+ case 'fail':
13
+ return 'Missing / invalid';
14
+ default:
15
+ return 'Unknown';
16
+ }
17
+ }
18
+ export function SecretsAuditCard({ secrets, auditEnabled, failedLogins24h, users, apiKeys, sources, onNavigate, }) {
19
+ const headingId = useId();
20
+ return (_jsxs(SecurityCard, { headingId: headingId, title: "Secrets, Encryption & Audit", description: "Platform secret status (values are never shown), audit logging, and cross-module risk.", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(StatusRow, { status: secrets.cmsSecret, label: "CMS signing secret", value: secretValue(secrets.cmsSecret), badge: _jsx(SettingsSourceBadge, { meta: sources.cmsSecret }) }), _jsx(StatusRow, { status: secrets.encryptionKey, label: "Encryption key", value: secretValue(secrets.encryptionKey), badge: _jsx(SettingsSourceBadge, { meta: sources.encryptionKey }) }), _jsx(StatusRow, { status: "pass", label: "API keys hashed at rest", value: "Yes" }), _jsx(StatusRow, { status: secrets.webhookSecretsEncrypted ? 'pass' : 'warning', label: "Webhook secrets encrypted", value: secrets.webhookSecretsEncrypted ? 'Yes' : 'Plaintext fallback' })] }), _jsxs("div", { className: "border-border mt-4 space-y-2 border-t pt-4", children: [_jsx(StatusRow, { status: auditEnabled ? 'pass' : 'warning', label: "Audit logging", value: auditEnabled ? 'Enabled' : 'Disabled', badge: _jsx(SettingsSourceBadge, { meta: sources.audit }) }), _jsx(StatusRow, { status: failedLogins24h >= 20 ? 'warning' : 'pass', label: "Failed logins (24h)", value: String(failedLogins24h) })] }), _jsxs("div", { className: "border-border mt-4 space-y-3 border-t pt-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "text-foreground font-medium", children: "Users" }), _jsxs("p", { className: "text-muted-foreground", children: [users.totalAdmins, " admin", users.totalAdmins === 1 ? '' : 's', ",", ' ', users.adminsWithoutMfa, " without MFA", users.inactive > 0 ? `, ${users.inactive} inactive` : ''] })] }), onNavigate && (_jsx("button", { type: "button", onClick: () => onNavigate('/settings?tab=users'), className: "text-brand shrink-0 text-sm hover:underline", children: "Review users \u2192" }))] }), _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "text-foreground font-medium", children: "API keys" }), _jsxs("p", { className: "text-muted-foreground", children: [apiKeys.active, " active, ", apiKeys.broadScope, " broad-scope, ", apiKeys.withoutExpiration, ' ', "without expiry"] })] }), onNavigate && (_jsx("button", { type: "button", onClick: () => onNavigate('/settings?tab=api-keys'), className: "text-brand shrink-0 text-sm hover:underline", children: "Review API keys \u2192" }))] })] })] }));
21
+ }
22
+ //# sourceMappingURL=SecretsAuditCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecretsAuditCard.js","sourceRoot":"","sources":["../../../src/views/settings/SecretsAuditCard.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAQ7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAEjE,SAAS,WAAW,CAAC,MAAmB;IACtC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,YAAY,CAAA;QACrB,KAAK,SAAS;YACZ,OAAO,gBAAgB,CAAA;QACzB,KAAK,MAAM;YACT,OAAO,mBAAmB,CAAA;QAC5B;YACE,OAAO,SAAS,CAAA;IACpB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAC/B,OAAO,EACP,YAAY,EACZ,eAAe,EACf,KAAK,EACL,OAAO,EACP,OAAO,EACP,UAAU,GASX;IACC,MAAM,SAAS,GAAG,KAAK,EAAE,CAAA;IAEzB,OAAO,CACL,MAAC,YAAY,IACX,SAAS,EAAE,SAAS,EACpB,KAAK,EAAC,6BAA6B,EACnC,WAAW,EAAC,wFAAwF,aAEpG,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,SAAS,IACR,MAAM,EAAE,OAAO,CAAC,SAAS,EACzB,KAAK,EAAC,oBAAoB,EAC1B,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,EACrC,KAAK,EAAE,KAAC,mBAAmB,IAAC,IAAI,EAAE,OAAO,CAAC,SAAS,GAAI,GACvD,EACF,KAAC,SAAS,IACR,MAAM,EAAE,OAAO,CAAC,aAAa,EAC7B,KAAK,EAAC,gBAAgB,EACtB,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC,EACzC,KAAK,EAAE,KAAC,mBAAmB,IAAC,IAAI,EAAE,OAAO,CAAC,aAAa,GAAI,GAC3D,EACF,KAAC,SAAS,IAAC,MAAM,EAAC,MAAM,EAAC,KAAK,EAAC,yBAAyB,EAAC,KAAK,EAAC,KAAK,GAAG,EACvE,KAAC,SAAS,IACR,MAAM,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAC5D,KAAK,EAAC,2BAA2B,EACjC,KAAK,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,GACrE,IACE,EAEN,eAAK,SAAS,EAAC,4CAA4C,aACzD,KAAC,SAAS,IACR,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EACzC,KAAK,EAAC,eAAe,EACrB,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,EAC5C,KAAK,EAAE,KAAC,mBAAmB,IAAC,IAAI,EAAE,OAAO,CAAC,KAAK,GAAI,GACnD,EACF,KAAC,SAAS,IACR,MAAM,EAAE,eAAe,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,EAClD,KAAK,EAAC,qBAAqB,EAC3B,KAAK,EAAE,MAAM,CAAC,eAAe,CAAC,GAC9B,IACE,EAGN,eAAK,SAAS,EAAC,4CAA4C,aACzD,eAAK,SAAS,EAAC,wCAAwC,aACrD,eAAK,SAAS,EAAC,SAAS,aACtB,YAAG,SAAS,EAAC,6BAA6B,sBAAU,EACpD,aAAG,SAAS,EAAC,uBAAuB,aACjC,KAAK,CAAC,WAAW,YAAQ,KAAK,CAAC,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,OAAG,GAAG,EACjE,KAAK,CAAC,gBAAgB,kBACtB,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,QAAQ,WAAW,CAAC,CAAC,CAAC,EAAE,IACvD,IACA,EACL,UAAU,IAAI,CACb,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,qBAAqB,CAAC,EAChD,SAAS,EAAC,6CAA6C,oCAGhD,CACV,IACG,EACN,eAAK,SAAS,EAAC,wCAAwC,aACrD,eAAK,SAAS,EAAC,SAAS,aACtB,YAAG,SAAS,EAAC,6BAA6B,yBAAa,EACvD,aAAG,SAAS,EAAC,uBAAuB,aACjC,OAAO,CAAC,MAAM,eAAW,OAAO,CAAC,UAAU,oBAAgB,OAAO,CAAC,iBAAiB,EAAE,GAAG,sBAExF,IACA,EACL,UAAU,IAAI,CACb,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,wBAAwB,CAAC,EACnD,SAAS,EAAC,6CAA6C,uCAGhD,CACV,IACG,IACF,IACO,CAChB,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { SecurityStatus } from '@actuate-media/cms-core/security-center';
2
+ export declare function SecurityStatusCard({ status, onNavigate, }: {
3
+ status: SecurityStatus;
4
+ onNavigate?: (path: string) => void;
5
+ }): import("react/jsx-runtime").JSX.Element;
6
+ //# sourceMappingURL=SecurityStatusCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecurityStatusCard.d.ts","sourceRoot":"","sources":["../../../src/views/settings/SecurityStatusCard.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,yCAAyC,CAAA;AA+B9F,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,UAAU,GACX,EAAE;IACD,MAAM,EAAE,cAAc,CAAA;IACtB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,2CAoEA"}
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId } from 'react';
4
+ import { Shield, ShieldAlert, ShieldCheck, ShieldX } from 'lucide-react';
5
+ import { CheckIcon } from './securityPrimitives.js';
6
+ const LEVEL_STYLES = {
7
+ secure: {
8
+ pill: 'bg-success/15 text-success border-success/30',
9
+ Icon: ShieldCheck,
10
+ word: 'Secure',
11
+ },
12
+ needs_attention: {
13
+ pill: 'bg-warning/15 text-warning border-warning/30',
14
+ Icon: ShieldAlert,
15
+ word: 'Needs attention',
16
+ },
17
+ at_risk: {
18
+ pill: 'bg-destructive/15 text-destructive border-destructive/30',
19
+ Icon: ShieldX,
20
+ word: 'At risk',
21
+ },
22
+ unknown: { pill: 'bg-muted text-muted-foreground border-border', Icon: Shield, word: 'Unknown' },
23
+ };
24
+ const SEVERITY_STYLES = {
25
+ critical: 'border-destructive/30 bg-destructive/10 text-destructive',
26
+ warning: 'border-warning/30 bg-warning/10 text-warning',
27
+ info: 'border-border bg-muted text-muted-foreground',
28
+ };
29
+ export function SecurityStatusCard({ status, onNavigate, }) {
30
+ const headingId = useId();
31
+ const style = LEVEL_STYLES[status.status];
32
+ const { Icon } = style;
33
+ return (_jsxs("section", { "aria-labelledby": headingId, className: "border-border bg-card rounded-lg border p-4", children: [_jsxs("div", { className: "mb-1 flex items-start justify-between gap-3", children: [_jsx("h3", { id: headingId, className: "text-foreground text-base font-medium", children: "Security Status" }), _jsxs("span", { className: `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-sm font-medium ${style.pill}`, children: [_jsx(Icon, { size: 15, "aria-hidden": "true" }), style.word] })] }), _jsx("p", { className: "text-muted-foreground mb-4 text-sm", children: status.description }), _jsx("div", { className: "grid grid-cols-1 gap-x-6 gap-y-1 sm:grid-cols-2", children: status.checks.map((check) => (_jsxs("button", { type: "button", disabled: !check.actionHref || !onNavigate, onClick: () => check.actionHref && onNavigate?.(check.actionHref), className: "enabled:hover:bg-accent/50 flex items-center justify-between gap-3 rounded-md py-1.5 text-left text-sm disabled:cursor-default", children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(CheckIcon, { status: check.status }), _jsx("span", { className: "text-foreground", children: check.label })] }), check.value !== undefined && (_jsx("span", { className: "text-muted-foreground text-right", children: check.value }))] }, check.key))) }), status.warnings.length > 0 && (_jsx("div", { className: "mt-4 space-y-2", "aria-live": "polite", children: status.warnings.map((w) => (_jsxs("div", { role: "note", className: `flex items-start justify-between gap-3 rounded-lg border p-3 text-sm ${SEVERITY_STYLES[w.severity]}`, children: [_jsxs("div", { children: [_jsx("p", { className: "font-medium", children: w.title }), _jsx("p", { className: "mt-0.5 opacity-90", children: w.description })] }), w.actionHref && w.actionLabel && onNavigate && (_jsx("button", { type: "button", onClick: () => onNavigate(w.actionHref), className: "shrink-0 self-center rounded-md border border-current/30 px-2.5 py-1 text-xs font-medium hover:opacity-80", children: w.actionLabel }))] }, w.id))) }))] }));
34
+ }
35
+ //# sourceMappingURL=SecurityStatusCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecurityStatusCard.js","sourceRoot":"","sources":["../../../src/views/settings/SecurityStatusCard.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAC7B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAExE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAEnD,MAAM,YAAY,GAGd;IACF,MAAM,EAAE;QACN,IAAI,EAAE,8CAA8C;QACpD,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,QAAQ;KACf;IACD,eAAe,EAAE;QACf,IAAI,EAAE,8CAA8C;QACpD,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,iBAAiB;KACxB;IACD,OAAO,EAAE;QACP,IAAI,EAAE,0DAA0D;QAChE,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;KAChB;IACD,OAAO,EAAE,EAAE,IAAI,EAAE,8CAA8C,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;CACjG,CAAA;AAED,MAAM,eAAe,GAAgD;IACnE,QAAQ,EAAE,0DAA0D;IACpE,OAAO,EAAE,8CAA8C;IACvD,IAAI,EAAE,8CAA8C;CACrD,CAAA;AAED,MAAM,UAAU,kBAAkB,CAAC,EACjC,MAAM,EACN,UAAU,GAIX;IACC,MAAM,SAAS,GAAG,KAAK,EAAE,CAAA;IACzB,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACzC,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAA;IAEtB,OAAO,CACL,sCAA0B,SAAS,EAAE,SAAS,EAAC,6CAA6C,aAC1F,eAAK,SAAS,EAAC,6CAA6C,aAC1D,aAAI,EAAE,EAAE,SAAS,EAAE,SAAS,EAAC,uCAAuC,gCAE/D,EACL,gBACE,SAAS,EAAE,wFAAwF,KAAK,CAAC,IAAI,EAAE,aAE/G,KAAC,IAAI,IAAC,IAAI,EAAE,EAAE,iBAAc,MAAM,GAAG,EAEpC,KAAK,CAAC,IAAI,IACN,IACH,EACN,YAAG,SAAS,EAAC,oCAAoC,YAAE,MAAM,CAAC,WAAW,GAAK,EAE1E,cAAK,SAAS,EAAC,iDAAiD,YAC7D,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAC5B,kBAEE,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,UAAU,EAC1C,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,IAAI,UAAU,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EACjE,SAAS,EAAC,gIAAgI,aAE1I,gBAAM,SAAS,EAAC,yBAAyB,aACvC,KAAC,SAAS,IAAC,MAAM,EAAE,KAAK,CAAC,MAAM,GAAI,EACnC,eAAM,SAAS,EAAC,iBAAiB,YAAE,KAAK,CAAC,KAAK,GAAQ,IACjD,EACN,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,CAC5B,eAAM,SAAS,EAAC,kCAAkC,YAAE,KAAK,CAAC,KAAK,GAAQ,CACxE,KAZI,KAAK,CAAC,GAAG,CAaP,CACV,CAAC,GACE,EAEL,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAC7B,cAAK,SAAS,EAAC,gBAAgB,eAAW,QAAQ,YAC/C,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAC1B,eAEE,IAAI,EAAC,MAAM,EACX,SAAS,EAAE,wEAAwE,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,aAEhH,0BACE,YAAG,SAAS,EAAC,aAAa,YAAE,CAAC,CAAC,KAAK,GAAK,EACxC,YAAG,SAAS,EAAC,mBAAmB,YAAE,CAAC,CAAC,WAAW,GAAK,IAChD,EACL,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,WAAW,IAAI,UAAU,IAAI,CAC9C,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,UAAW,CAAC,EACxC,SAAS,EAAC,2GAA2G,YAEpH,CAAC,CAAC,WAAW,GACP,CACV,KAhBI,CAAC,CAAC,EAAE,CAiBL,CACP,CAAC,GACE,CACP,IACO,CACX,CAAA;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function SecurityTab({ role, onNavigate, }: {
2
+ role?: string;
3
+ onNavigate?: (path: string) => void;
4
+ }): import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=SecurityTab.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecurityTab.d.ts","sourceRoot":"","sources":["../../../src/views/settings/SecurityTab.tsx"],"names":[],"mappings":"AAyBA,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,UAAU,GACX,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,2CAmIA"}
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect } from 'react';
4
+ import { ShieldAlert } from 'lucide-react';
5
+ import { SettingsSaveBar, SettingsValidationSummary } from './components.js';
6
+ import { AccessProtectionCard } from './AccessProtectionCard.js';
7
+ import { AuthMfaCard } from './AuthMfaCard.js';
8
+ import { SecretsAuditCard } from './SecretsAuditCard.js';
9
+ import { SecurityStatusCard } from './SecurityStatusCard.js';
10
+ import { SessionPolicyCard } from './SessionPolicyCard.js';
11
+ import { useSecuritySettings } from './useSecuritySettings.js';
12
+ function CardSkeleton() {
13
+ return (_jsxs("div", { className: "border-border bg-card animate-pulse rounded-lg border p-4", children: [_jsx("div", { className: "bg-muted mb-3 h-4 w-40 rounded" }), _jsx("div", { className: "bg-muted mb-4 h-3 w-72 rounded" }), _jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "bg-muted h-3 w-full rounded" }), _jsx("div", { className: "bg-muted h-3 w-2/3 rounded" })] })] }));
14
+ }
15
+ export function SecurityTab({ role, onNavigate, }) {
16
+ const canEdit = role === 'ADMIN';
17
+ const { loading, loadError, data, form, setField, dirty, issues, hasErrors, saveState, save, reset, revokeState, revokeAllOtherSessions, } = useSecuritySettings(canEdit);
18
+ // Deep-link support: /settings?tab=security&section=sessions|ip-allowlist|audit
19
+ useEffect(() => {
20
+ if (loading || typeof window === 'undefined')
21
+ return;
22
+ const section = new URLSearchParams(window.location.search).get('section');
23
+ if (!section)
24
+ return;
25
+ const el = document.getElementById(`security-${section}`);
26
+ el?.scrollIntoView({ behavior: 'smooth', block: 'start' });
27
+ }, [loading]);
28
+ if (loading) {
29
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CardSkeleton, {}), _jsx(CardSkeleton, {}), _jsx(CardSkeleton, {})] }));
30
+ }
31
+ if (loadError || !data) {
32
+ const forbidden = /forbidden|permission|insufficient/i.test(loadError ?? '');
33
+ return (_jsxs("div", { className: "border-border bg-card text-muted-foreground flex items-start gap-3 rounded-lg border p-6 text-sm", children: [_jsx(ShieldAlert, { size: 20, className: "text-warning mt-0.5 shrink-0", "aria-hidden": "true" }), _jsxs("div", { children: [_jsx("p", { className: "text-foreground font-medium", children: forbidden
34
+ ? 'You do not have access to security settings'
35
+ : 'Security settings unavailable' }), _jsx("p", { className: "mt-1", children: forbidden
36
+ ? 'Ask an administrator to review the Security tab.'
37
+ : (loadError ?? 'The security service could not be reached. Try again shortly.') })] })] }));
38
+ }
39
+ return (_jsxs("div", { className: "space-y-4 pb-4", children: [_jsx(SecurityStatusCard, { status: data.status, onNavigate: onNavigate }), issues.length > 0 && _jsx(SettingsValidationSummary, { issues: issues }), !canEdit && (_jsx("p", { className: "text-muted-foreground text-sm", children: "You have read-only access. Only administrators can change security settings." })), _jsx(AuthMfaCard, { form: form, setField: setField, canEdit: canEdit, viewerHasMfa: data.viewerHasMfa, withMfa: data.users.withMfa, totalAdmins: data.users.totalAdmins, onNavigate: onNavigate }), _jsx("div", { id: "security-sessions", children: _jsx(SessionPolicyCard, { form: form, setField: setField, canEdit: canEdit, sessionMaxAgeHours: data.sessionMaxAgeHours, activeSessions: data.activeSessions, revokeState: revokeState, onRevokeAll: () => void revokeAllOtherSessions() }) }), _jsx("div", { id: "security-ip-allowlist", children: _jsx(AccessProtectionCard, { ipAllowlist: data.ipAllowlist, secrets: data.secrets, csrfEnabled: data.csrfEnabled, uploadValidation: data.uploadValidation, sources: data.sources, onNavigate: onNavigate }) }), _jsx("div", { id: "security-audit", children: _jsx(SecretsAuditCard, { secrets: data.secrets, auditEnabled: data.auditEnabled, failedLogins24h: data.failedLogins24h, users: data.users, apiKeys: data.apiKeys, sources: data.sources, onNavigate: onNavigate }) }), saveState === 'error' && (_jsx("p", { className: "text-destructive text-sm", role: "alert", children: "Failed to save security settings. Please try again." })), _jsx(SettingsSaveBar, { dirty: dirty, canEdit: canEdit, hasErrors: hasErrors, saveState: saveState, onSave: () => void save(), onReset: reset })] }));
40
+ }
41
+ //# sourceMappingURL=SecurityTab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecurityTab.js","sourceRoot":"","sources":["../../../src/views/settings/SecurityTab.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { SecurityForm } from './useSecuritySettings.js';
2
+ export declare function SessionPolicyCard({ form, setField, canEdit, sessionMaxAgeHours, activeSessions, revokeState, onRevokeAll, }: {
3
+ form: SecurityForm;
4
+ setField: <K extends keyof SecurityForm>(key: K, value: SecurityForm[K]) => void;
5
+ canEdit: boolean;
6
+ sessionMaxAgeHours: number;
7
+ activeSessions: number;
8
+ revokeState: 'idle' | 'working' | 'done' | 'error';
9
+ onRevokeAll: () => void;
10
+ }): import("react/jsx-runtime").JSX.Element;
11
+ //# sourceMappingURL=SessionPolicyCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionPolicyCard.d.ts","sourceRoot":"","sources":["../../../src/views/settings/SessionPolicyCard.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAM5D,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,YAAY,CAAA;IAClB,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,YAAY,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAChF,OAAO,EAAE,OAAO,CAAA;IAChB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAA;IAClD,WAAW,EAAE,MAAM,IAAI,CAAA;CACxB,2CA6FA"}
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId, useState } from 'react';
4
+ import { Loader2 } from 'lucide-react';
5
+ import { MAX_CONCURRENT_SESSION_OPTIONS } from '@actuate-media/cms-core/security-center';
6
+ import { ConfirmDangerousSettingDialog } from './components.js';
7
+ import { SecurityCard, StatusRow } from './securityPrimitives.js';
8
+ function sessionsLabel(n) {
9
+ return n === 0 ? 'Unlimited' : String(n);
10
+ }
11
+ export function SessionPolicyCard({ form, setField, canEdit, sessionMaxAgeHours, activeSessions, revokeState, onRevokeAll, }) {
12
+ const headingId = useId();
13
+ const maxId = useId();
14
+ const [confirmRevoke, setConfirmRevoke] = useState(false);
15
+ const maxAgeDays = Math.round(sessionMaxAgeHours / 24);
16
+ return (_jsxs(SecurityCard, { headingId: headingId, title: "Session Policy", description: "Control how many sessions an account can hold and revoke active sessions.", children: [_jsx("div", { className: "space-y-4", children: _jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("label", { htmlFor: maxId, className: "flex-1", children: [_jsx("span", { className: "text-foreground text-base font-medium", children: "Maximum concurrent sessions" }), _jsx("p", { className: "text-muted-foreground mt-0.5 text-sm", children: "Oldest sessions are signed out when a user exceeds this. Choose Unlimited to disable the cap." })] }), _jsx("select", { id: maxId, value: form.maxConcurrentSessions, disabled: !canEdit, onChange: (e) => setField('maxConcurrentSessions', Number(e.target.value)), className: "border-border bg-input-background text-foreground focus-visible:ring-ring rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50", children: MAX_CONCURRENT_SESSION_OPTIONS.map((n) => (_jsx("option", { value: n, children: sessionsLabel(n) }, n))) })] }) }), _jsxs("div", { className: "border-border mt-4 space-y-2 border-t pt-4", children: [_jsx(StatusRow, { status: "pass", label: "Active admin sessions", value: String(activeSessions) }), _jsx(StatusRow, { status: "pass", label: "Maximum session age", value: `${maxAgeDays} day${maxAgeDays === 1 ? '' : 's'}` }), _jsx(StatusRow, { status: "pass", label: "Revoke sessions on password change", value: "Enabled" })] }), _jsxs("div", { className: "border-border mt-4 flex items-center justify-between gap-3 border-t pt-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: "Revoke other sessions" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Sign out every session except this one. Useful after a suspected compromise." }), revokeState === 'done' && (_jsx("p", { className: "text-success mt-1 text-sm", "aria-live": "polite", children: "Other sessions revoked." })), revokeState === 'error' && (_jsx("p", { className: "text-destructive mt-1 text-sm", "aria-live": "polite", children: "Could not revoke sessions. Try again." }))] }), _jsxs("button", { type: "button", disabled: !canEdit || revokeState === 'working', onClick: () => setConfirmRevoke(true), className: "border-border text-foreground hover:bg-accent inline-flex shrink-0 items-center gap-2 rounded-md border px-3 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50", children: [revokeState === 'working' && (_jsx(Loader2, { size: 15, className: "animate-spin", "aria-hidden": "true" })), "Revoke other sessions"] })] }), _jsx(ConfirmDangerousSettingDialog, { open: confirmRevoke, title: "Revoke all other sessions?", description: "Every active session except your current one will be signed out immediately. Those users (and your other devices) will need to sign in again.", confirmLabel: "Revoke sessions", onConfirm: () => {
17
+ setConfirmRevoke(false);
18
+ onRevokeAll();
19
+ }, onCancel: () => setConfirmRevoke(false) })] }));
20
+ }
21
+ //# sourceMappingURL=SessionPolicyCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionPolicyCard.js","sourceRoot":"","sources":["../../../src/views/settings/SessionPolicyCard.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { CheckStatus } from '@actuate-media/cms-core/security-center';
3
+ /** Small status icon used in check/summary rows. Text label always accompanies it. */
4
+ export declare function CheckIcon({ status }: {
5
+ status: CheckStatus;
6
+ }): import("react/jsx-runtime").JSX.Element;
7
+ /** A status row: icon + label on the left, value on the right. */
8
+ export declare function StatusRow({ status, label, value, badge, }: {
9
+ status: CheckStatus;
10
+ label: string;
11
+ value?: ReactNode;
12
+ badge?: ReactNode;
13
+ }): import("react/jsx-runtime").JSX.Element;
14
+ /** Section wrapper matching the other settings cards. */
15
+ export declare function SecurityCard({ headingId, title, description, children, }: {
16
+ headingId: string;
17
+ title: string;
18
+ description: string;
19
+ children: ReactNode;
20
+ }): import("react/jsx-runtime").JSX.Element;
21
+ //# sourceMappingURL=securityPrimitives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"securityPrimitives.d.ts","sourceRoot":"","sources":["../../../src/views/settings/securityPrimitives.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAA;AAE1E,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,2CAW5D;AASD,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,EACxB,MAAM,EACN,KAAK,EACL,KAAK,EACL,KAAK,GACN,EAAE;IACD,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,2CAaA;AAED,yDAAyD;AACzD,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,KAAK,EACL,WAAW,EACX,QAAQ,GACT,EAAE;IACD,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,SAAS,CAAA;CACpB,2CAUA"}
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { AlertTriangle, Check, CircleHelp, X } from 'lucide-react';
4
+ /** Small status icon used in check/summary rows. Text label always accompanies it. */
5
+ export function CheckIcon({ status }) {
6
+ switch (status) {
7
+ case 'pass':
8
+ return _jsx(Check, { size: 15, className: "text-success shrink-0", "aria-hidden": "true" });
9
+ case 'warning':
10
+ return _jsx(AlertTriangle, { size: 15, className: "text-warning shrink-0", "aria-hidden": "true" });
11
+ case 'fail':
12
+ return _jsx(X, { size: 15, className: "text-destructive shrink-0", "aria-hidden": "true" });
13
+ default:
14
+ return _jsx(CircleHelp, { size: 15, className: "text-muted-foreground shrink-0", "aria-hidden": "true" });
15
+ }
16
+ }
17
+ const STATUS_WORD = {
18
+ pass: 'Pass',
19
+ warning: 'Warning',
20
+ fail: 'Fail',
21
+ unknown: 'Unknown',
22
+ };
23
+ /** A status row: icon + label on the left, value on the right. */
24
+ export function StatusRow({ status, label, value, badge, }) {
25
+ return (_jsxs("div", { className: "flex items-center justify-between gap-3 py-1.5 text-sm", children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(CheckIcon, { status: status }), _jsxs("span", { className: "sr-only", children: [STATUS_WORD[status], ": "] }), _jsx("span", { className: "text-foreground", children: label }), badge] }), value !== undefined && _jsx("span", { className: "text-muted-foreground text-right", children: value })] }));
26
+ }
27
+ /** Section wrapper matching the other settings cards. */
28
+ export function SecurityCard({ headingId, title, description, children, }) {
29
+ return (_jsxs("section", { "aria-labelledby": headingId, className: "border-border bg-card rounded-lg border p-4", children: [_jsx("h3", { id: headingId, className: "text-foreground mb-1 text-base font-medium", children: title }), _jsx("p", { className: "text-muted-foreground mb-4 text-sm", children: description }), children] }));
30
+ }
31
+ //# sourceMappingURL=securityPrimitives.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"securityPrimitives.js","sourceRoot":"","sources":["../../../src/views/settings/securityPrimitives.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAIlE,sFAAsF;AACtF,MAAM,UAAU,SAAS,CAAC,EAAE,MAAM,EAA2B;IAC3D,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAC,KAAK,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,uBAAuB,iBAAa,MAAM,GAAG,CAAA;QACjF,KAAK,SAAS;YACZ,OAAO,KAAC,aAAa,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,uBAAuB,iBAAa,MAAM,GAAG,CAAA;QACzF,KAAK,MAAM;YACT,OAAO,KAAC,CAAC,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,2BAA2B,iBAAa,MAAM,GAAG,CAAA;QACjF;YACE,OAAO,KAAC,UAAU,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,gCAAgC,iBAAa,MAAM,GAAG,CAAA;IACjG,CAAC;AACH,CAAC;AAED,MAAM,WAAW,GAAgC;IAC/C,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;CACnB,CAAA;AAED,kEAAkE;AAClE,MAAM,UAAU,SAAS,CAAC,EACxB,MAAM,EACN,KAAK,EACL,KAAK,EACL,KAAK,GAMN;IACC,OAAO,CACL,eAAK,SAAS,EAAC,wDAAwD,aACrE,gBAAM,SAAS,EAAC,yBAAyB,aACvC,KAAC,SAAS,IAAC,MAAM,EAAE,MAAM,GAAI,EAE7B,gBAAM,SAAS,EAAC,SAAS,aAAE,WAAW,CAAC,MAAM,CAAC,UAAU,EACxD,eAAM,SAAS,EAAC,iBAAiB,YAAE,KAAK,GAAQ,EAC/C,KAAK,IACD,EACN,KAAK,KAAK,SAAS,IAAI,eAAM,SAAS,EAAC,kCAAkC,YAAE,KAAK,GAAQ,IACrF,CACP,CAAA;AACH,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,YAAY,CAAC,EAC3B,SAAS,EACT,KAAK,EACL,WAAW,EACX,QAAQ,GAMT;IACC,OAAO,CACL,sCAA0B,SAAS,EAAE,SAAS,EAAC,6CAA6C,aAC1F,aAAI,EAAE,EAAE,SAAS,EAAE,SAAS,EAAC,4CAA4C,YACtE,KAAK,GACH,EACL,YAAG,SAAS,EAAC,oCAAoC,YAAE,WAAW,GAAK,EAClE,QAAQ,IACD,CACX,CAAA;AACH,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type SecurityCenterData } from '@actuate-media/cms-core/security-center';
2
+ import type { ValidationIssue } from './useGeneralSettings.js';
3
+ /** The subset of security policy the tab can actually edit + enforce. */
4
+ export interface SecurityForm {
5
+ mfaRequired: boolean;
6
+ mfaGracePeriodDays: number;
7
+ maxConcurrentSessions: number;
8
+ }
9
+ type SaveState = 'idle' | 'saving' | 'success' | 'error';
10
+ export interface UseSecuritySettings {
11
+ loading: boolean;
12
+ loadError: string | null;
13
+ data: SecurityCenterData | null;
14
+ form: SecurityForm;
15
+ setField: <K extends keyof SecurityForm>(key: K, value: SecurityForm[K]) => void;
16
+ dirty: boolean;
17
+ issues: ValidationIssue[];
18
+ hasErrors: boolean;
19
+ canEdit: boolean;
20
+ saveState: SaveState;
21
+ saveError: string | null;
22
+ save: () => Promise<void>;
23
+ reset: () => void;
24
+ refetch: () => void;
25
+ revokeState: 'idle' | 'working' | 'done' | 'error';
26
+ revokeAllOtherSessions: () => Promise<number | null>;
27
+ }
28
+ /**
29
+ * Loads the Security Center (`GET /security`) — computed posture, cross-module
30
+ * summaries, secret/encryption verdicts — and tracks the small editable policy
31
+ * (MFA requirement + grace, concurrent-session cap). Save is lockout-safe: the
32
+ * client mirrors the server's MFA guard so the operator gets immediate feedback,
33
+ * and the server re-checks authoritatively.
34
+ */
35
+ export declare function useSecuritySettings(canEdit: boolean): UseSecuritySettings;
36
+ export {};
37
+ //# sourceMappingURL=useSecuritySettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSecuritySettings.d.ts","sourceRoot":"","sources":["../../../src/views/settings/useSecuritySettings.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,yCAAyC,CAAA;AAEhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAE9D,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,OAAO,CAAA;IACpB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,qBAAqB,EAAE,MAAM,CAAA;CAC9B;AAED,KAAK,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAA;AAuBxD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC/B,IAAI,EAAE,YAAY,CAAA;IAClB,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,YAAY,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAChF,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,SAAS,CAAA;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAA;IAClD,sBAAsB,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CACrD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAiIzE"}