import assert from 'node:assert/strict'; import test from 'node:test'; import { isLegacyPageDataApiPath } from '../page-data-routes.mjs'; import { isPageDataPublicPath } from '../page-data-public-service.mjs'; import { assessPortalAccessMigrationReadiness, classifyPortalApiAccess, comparePortalAccessPolicyShadow, createPortalAccessShadowReporter, findPortalStaticAccessRule, isPortalLegacyDirectGlobalAuthBypass, isPortalPlazaOptionalUserPath, PORTAL_ACCESS_CLASS, PORTAL_ACCESS_ENFORCEMENT_GROUPS, PORTAL_STATIC_ACCESS_RULES, resolvePortalAccessEnforcementConfig, resolvePortalAccessEnforcementDecision, resolvePortalAccessPolicyMode, resolvePortalAccessShadowLogIntervalMs, shouldOverridePortalLegacyGlobalAuth, } from './portal-access-policy.mjs'; const pageDataPredicates = { isPageDataPublicPath, isLegacyPageDataApiPath, }; const boundaryCases = [ ['/status', 'GET', PORTAL_ACCESS_CLASS.PUBLIC, true], ['/runtime/status', 'POST', PORTAL_ACCESS_CLASS.PUBLIC, true], ['/config/blocked-words', 'GET', PORTAL_ACCESS_CLASS.PUBLIC, true], ['/internal/agent/jobs/job-1/claim', 'POST', PORTAL_ACCESS_CLASS.INTERNAL, true], ['/internal/deep-search/llm', 'POST', PORTAL_ACCESS_CLASS.INTERNAL, true], ['/agent/mindspace_page_patch', 'POST', PORTAL_ACCESS_CLASS.INTERNAL, true], ['/agent/mindspace_asset_delete', 'POST', PORTAL_ACCESS_CLASS.INTERNAL, true], ['/agent/mindspace_asset_download', 'POST', PORTAL_ACCESS_CLASS.INTERNAL, true], ['/agent/mindspace_image_generate', 'POST', PORTAL_ACCESS_CLASS.INTERNAL, true], ['/internal/image-make/runtime-config', 'GET', PORTAL_ACCESS_CLASS.INTERNAL, true], ['/mindspace/v1/assets/asset-1/download', 'GET', PORTAL_ACCESS_CLASS.ROUTE_AUTHORIZED, true], ['/mindspace/v1/assets/asset-1/download', 'POST', PORTAL_ACCESS_CLASS.AUTHENTICATED, false], ['/plaza/v1/categories', 'GET', PORTAL_ACCESS_CLASS.OPTIONAL_USER, false], ['/plaza/v1/feed', 'get', PORTAL_ACCESS_CLASS.OPTIONAL_USER, false], ['/plaza/v1/posts/post-1', 'GET', PORTAL_ACCESS_CLASS.OPTIONAL_USER, false], ['/plaza/v1/posts/post-1/comments', 'GET', PORTAL_ACCESS_CLASS.OPTIONAL_USER, false], ['/plaza/v1/users/alice', 'GET', PORTAL_ACCESS_CLASS.OPTIONAL_USER, false], ['/plaza/v1/users/alice/posts', 'GET', PORTAL_ACCESS_CLASS.OPTIONAL_USER, false], ['/plaza/v1/events', 'POST', PORTAL_ACCESS_CLASS.OPTIONAL_USER, false], ['/plaza/v1/posts/post-1/reports', 'POST', PORTAL_ACCESS_CLASS.AUTHENTICATED, false], [ '/public/pages/page-1/data/signups/rows', 'POST', PORTAL_ACCESS_CLASS.ROUTE_AUTHORIZED, false, ], ['/public/pages/page-1/data/signups', 'GET', PORTAL_ACCESS_CLASS.ROUTE_AUTHORIZED, false], ['/page-data/signups', 'GET', PORTAL_ACCESS_CLASS.PUBLIC, false], ['/mindspace/v1/pages', 'GET', PORTAL_ACCESS_CLASS.AUTHENTICATED, false], ]; test('static access registry has stable unique rule ids', () => { const ids = PORTAL_STATIC_ACCESS_RULES.map((rule) => rule.id); assert.equal(new Set(ids).size, ids.length); assert.ok(ids.length >= 10); for (const rule of PORTAL_STATIC_ACCESS_RULES) { assert.ok(Object.values(PORTAL_ACCESS_CLASS).includes(rule.accessClass), rule.id); } }); test('portal access policy classifies the existing route boundary', () => { for (const [path, method, expected] of boundaryCases) { assert.equal( classifyPortalApiAccess({ path, method }, pageDataPredicates), expected, `${method} ${path}`, ); } }); test('legacy direct bypass derives from the same access registry', () => { for (const [path, method, _expectedClass, expectedDirectBypass] of boundaryCases) { assert.equal( isPortalLegacyDirectGlobalAuthBypass(path, method), expectedDirectBypass, `${method} ${path}`, ); } assert.equal(findPortalStaticAccessRule({ path: '/mindspace/v1/pages', method: 'GET' }), null); }); test('Plaza optional-user matcher excludes authenticated write routes', () => { assert.equal(isPortalPlazaOptionalUserPath('/plaza/v1/seo/sitemap', 'GET'), true); assert.equal(isPortalPlazaOptionalUserPath('/plaza/v1/events', 'POST'), true); assert.equal(isPortalPlazaOptionalUserPath('/plaza/v1/posts/post-1', 'PATCH'), false); assert.equal(isPortalPlazaOptionalUserPath('/plaza/v1/posts/post-1/reports', 'POST'), false); assert.equal(isPortalPlazaOptionalUserPath('/mindspace/v1/pages', 'GET'), false); }); test('portal access policy flags are safely bounded', () => { assert.equal(resolvePortalAccessPolicyMode({}), 'off'); assert.equal( resolvePortalAccessPolicyMode({ MEMIND_PORTAL_ACCESS_POLICY_MODE: ' SHADOW ' }), 'shadow', ); assert.equal(resolvePortalAccessPolicyMode({ MEMIND_PORTAL_ACCESS_POLICY_MODE: 'on' }), 'off'); assert.equal(resolvePortalAccessPolicyMode({ MEMIND_PORTAL_ACCESS_POLICY_MODE: 'invalid' }), 'off'); assert.equal(resolvePortalAccessShadowLogIntervalMs({}), 60_000); assert.equal( resolvePortalAccessShadowLogIntervalMs({ MEMIND_PORTAL_ACCESS_SHADOW_LOG_INTERVAL_MS: '1', }), 1_000, ); assert.equal( resolvePortalAccessShadowLogIntervalMs({ MEMIND_PORTAL_ACCESS_SHADOW_LOG_INTERVAL_MS: '9999999', }), 3_600_000, ); }); test('access enforcement requires the master switch and an exact group', () => { const defaultConfig = resolvePortalAccessEnforcementConfig({}); assert.equal(defaultConfig.enabled, false); assert.deepEqual(defaultConfig.activeGroups, []); const groupOnly = resolvePortalAccessEnforcementConfig({ MEMIND_PORTAL_ACCESS_POLICY_ENFORCE_GROUPS: 'legacy-page-data', }); assert.equal(groupOnly.enabled, false); assert.deepEqual(groupOnly.requestedGroups, ['legacy-page-data']); const enabled = resolvePortalAccessEnforcementConfig({ MEMIND_PORTAL_ACCESS_POLICY_ENFORCEMENT_ENABLED: '1', MEMIND_PORTAL_ACCESS_POLICY_ENFORCE_GROUPS: ' legacy-page-data,legacy-page-data ', }); assert.equal(enabled.enabled, true); assert.deepEqual(enabled.activeGroups, ['legacy-page-data']); }); test('access enforcement rejects all and unknown groups when master is enabled', () => { assert.deepEqual(PORTAL_ACCESS_ENFORCEMENT_GROUPS, [ 'legacy-page-data', 'page-data-public', 'plaza-optional-user', ]); assert.throws( () => resolvePortalAccessEnforcementConfig({ MEMIND_PORTAL_ACCESS_POLICY_ENFORCEMENT_ENABLED: '1', MEMIND_PORTAL_ACCESS_POLICY_ENFORCE_GROUPS: 'all', }), /Unknown Portal access enforcement group\(s\): all/, ); assert.throws( () => resolvePortalAccessEnforcementConfig({ MEMIND_PORTAL_ACCESS_POLICY_ENFORCEMENT_ENABLED: '1', MEMIND_PORTAL_ACCESS_POLICY_ENFORCE_GROUPS: 'legacy-page-data,typo-group', }), /typo-group/, ); }); test('kill switch overrides every requested enforcement group', () => { const config = resolvePortalAccessEnforcementConfig({ MEMIND_PORTAL_ACCESS_POLICY_ENFORCEMENT_ENABLED: '1', MEMIND_PORTAL_ACCESS_POLICY_ENFORCE_GROUPS: PORTAL_ACCESS_ENFORCEMENT_GROUPS.join(','), MEMIND_PORTAL_ACCESS_POLICY_KILL_SWITCH: '1', }); assert.equal(config.masterEnabled, true); assert.equal(config.killSwitch, true); assert.equal(config.enabled, false); assert.deepEqual(config.activeGroups, []); }); test('single-group enforcement bypasses only the selected policy group', () => { const config = resolvePortalAccessEnforcementConfig({ MEMIND_PORTAL_ACCESS_POLICY_ENFORCEMENT_ENABLED: '1', MEMIND_PORTAL_ACCESS_POLICY_ENFORCE_GROUPS: 'legacy-page-data', }); const legacyDecision = resolvePortalAccessEnforcementDecision( { path: '/page-data/signups', method: 'GET' }, pageDataPredicates, config, ); assert.deepEqual(legacyDecision, { selected: true, bypassGlobalAuth: true, reason: 'group-enabled', proposedAccessClass: PORTAL_ACCESS_CLASS.PUBLIC, proposedRuleId: 'legacy-page-data', policyGroup: 'legacy-page-data', }); const publicPageDataDecision = resolvePortalAccessEnforcementDecision( { path: '/public/pages/page-1/data/signups', method: 'GET' }, pageDataPredicates, config, ); assert.equal(publicPageDataDecision.selected, false); assert.equal(publicPageDataDecision.bypassGlobalAuth, false); assert.equal(publicPageDataDecision.reason, 'group-disabled'); const privateDecision = resolvePortalAccessEnforcementDecision( { path: '/mindspace/v1/pages', method: 'GET' }, pageDataPredicates, config, ); assert.equal(privateDecision.selected, false); assert.equal(privateDecision.bypassGlobalAuth, false); assert.equal(privateDecision.policyGroup, 'authenticated-default'); }); test('enforcement preserves legacy optional-user hydration when already bypassed', () => { const config = resolvePortalAccessEnforcementConfig({ MEMIND_PORTAL_ACCESS_POLICY_ENFORCEMENT_ENABLED: '1', MEMIND_PORTAL_ACCESS_POLICY_ENFORCE_GROUPS: 'page-data-public', }); const decision = resolvePortalAccessEnforcementDecision( { path: '/public/pages/page-1/data/signups', method: 'GET' }, pageDataPredicates, config, ); assert.equal(decision.selected, true); assert.equal(decision.bypassGlobalAuth, true); assert.equal( shouldOverridePortalLegacyGlobalAuth(decision, false), true, 'no-auth and legacy modes need the new bypass', ); assert.equal( shouldOverridePortalLegacyGlobalAuth(decision, true), false, 'multi-user mode must continue through legacy hydration', ); }); test('enforcement decision reports kill switch and master-off reasons', () => { const request = { path: '/page-data/signups', method: 'GET' }; const groupOnly = resolvePortalAccessEnforcementConfig({ MEMIND_PORTAL_ACCESS_POLICY_ENFORCE_GROUPS: 'legacy-page-data', }); assert.equal( resolvePortalAccessEnforcementDecision(request, pageDataPredicates, groupOnly).reason, 'master-disabled', ); const killed = resolvePortalAccessEnforcementConfig({ MEMIND_PORTAL_ACCESS_POLICY_ENFORCEMENT_ENABLED: '1', MEMIND_PORTAL_ACCESS_POLICY_ENFORCE_GROUPS: 'legacy-page-data', MEMIND_PORTAL_ACCESS_POLICY_KILL_SWITCH: '1', }); assert.equal( resolvePortalAccessEnforcementDecision(request, pageDataPredicates, killed).reason, 'kill-switch', ); }); test('shadow comparison matches current direct bypass and authenticated routes', () => { const cases = [ { path: '/status', method: 'GET', multiUserEnabled: false }, { path: '/internal/agent/jobs/job-1/claim', method: 'POST', multiUserEnabled: false }, { path: '/mindspace/v1/assets/asset-1/download', method: 'GET', multiUserEnabled: false, }, { path: '/plaza/v1/categories', method: 'GET', multiUserEnabled: true }, { path: '/mindspace/v1/pages', method: 'GET', multiUserEnabled: false }, { path: '/mindspace/v1/pages', method: 'GET', multiUserEnabled: true }, ]; for (const request of cases) { const comparison = comparePortalAccessPolicyShadow(request, { ...pageDataPredicates, multiUserEnabled: request.multiUserEnabled, }); assert.equal(comparison.mismatch, false, `${request.method} ${request.path}`); } }); test('shadow comparison exposes public-route coupling to the multi-user auth mode', () => { const cases = [ ['/plaza/v1/categories', 'GET', PORTAL_ACCESS_CLASS.OPTIONAL_USER, 'plaza-optional-user'], [ '/public/pages/page-1/data/signups', 'GET', PORTAL_ACCESS_CLASS.ROUTE_AUTHORIZED, 'page-data-public', ], ['/page-data/signups', 'GET', PORTAL_ACCESS_CLASS.PUBLIC, 'legacy-page-data'], ]; for (const [path, method, expectedClass, expectedGroup] of cases) { const comparison = comparePortalAccessPolicyShadow( { path, method }, { ...pageDataPredicates, multiUserEnabled: false, }, ); assert.equal(comparison.proposedAccessClass, expectedClass); assert.equal(comparison.policyGroup, expectedGroup); assert.equal(comparison.currentGlobalAuthBypass, false); assert.equal(comparison.proposedGlobalAuthBypass, true); assert.equal(comparison.mismatch, true); } }); test('shadow comparison stays aligned for public routes in multi-user mode', () => { const comparison = comparePortalAccessPolicyShadow( { path: '/plaza/v1/categories', method: 'GET' }, { ...pageDataPredicates, multiUserEnabled: true, }, ); assert.deepEqual(comparison, { path: '/plaza/v1/categories', method: 'GET', proposedAccessClass: PORTAL_ACCESS_CLASS.OPTIONAL_USER, proposedRuleId: 'plaza-discovery', policyGroup: 'plaza-optional-user', currentGlobalAuthBypass: true, proposedGlobalAuthBypass: true, mismatch: false, }); }); test('migration readiness separates known differences from unexpected drift', () => { const requests = [ { path: '/plaza/v1/categories', method: 'GET' }, { path: '/public/pages/page-1/data/signups', method: 'GET' }, { path: '/page-data/signups', method: 'GET' }, { path: '/mindspace/v1/pages', method: 'GET' }, ]; const knownMismatchPolicyGroups = [ 'plaza-optional-user', 'page-data-public', 'legacy-page-data', ]; const report = assessPortalAccessMigrationReadiness(requests, { ...pageDataPredicates, multiUserEnabled: false, knownMismatchPolicyGroups, }); assert.equal(report.contractReady, true); assert.equal(report.safeForBehaviorSwitch, false); assert.equal(report.total, 4); assert.equal(report.aligned, 1); assert.equal(report.knownMismatches.length, 3); assert.equal(report.unexpectedMismatches.length, 0); const strictReport = assessPortalAccessMigrationReadiness(requests, { ...pageDataPredicates, multiUserEnabled: false, knownMismatchPolicyGroups: knownMismatchPolicyGroups.slice(0, 2), }); assert.equal(strictReport.contractReady, false); assert.equal(strictReport.unexpectedMismatches[0].policyGroup, 'legacy-page-data'); }); test('multi-user route contract is ready and aligned', () => { const report = assessPortalAccessMigrationReadiness( boundaryCases.map(([path, method]) => ({ path, method })), { ...pageDataPredicates, multiUserEnabled: true, }, ); assert.equal(report.contractReady, true); assert.equal(report.safeForBehaviorSwitch, true); assert.equal(report.aligned, boundaryCases.length); }); test('shadow reporter groups dynamic routes, suppresses repeats, and preserves counts', () => { let currentTime = 10_000; const emitted = []; const reporter = createPortalAccessShadowReporter({ intervalMs: 1_000, now: () => currentTime, emit: (event) => emitted.push(event), }); const mismatch = { authMode: 'none', method: 'GET', proposedAccessClass: PORTAL_ACCESS_CLASS.OPTIONAL_USER, policyGroup: 'plaza-optional-user', currentGlobalAuthBypass: false, proposedGlobalAuthBypass: true, mismatch: true, }; reporter.record({ ...mismatch, requestId: 'req-1', path: '/plaza/v1/posts/post-1', proposedRuleId: 'plaza-post-detail', }); currentTime += 500; reporter.record({ ...mismatch, requestId: 'req-2', path: '/plaza/v1/users/alice', proposedRuleId: 'plaza-user-profile', }); assert.equal(emitted.length, 1); assert.deepEqual(reporter.snapshot(), [{ key: 'none:GET:plaza-optional-user', totalOccurrences: 2, suppressedSinceLast: 1, lastEmittedAt: 10_000, }]); currentTime += 501; const next = reporter.record({ ...mismatch, requestId: 'req-3', path: '/plaza/v1/feed', proposedRuleId: 'plaza-discovery', }); assert.equal(emitted.length, 2); assert.equal(next.occurrences, 3); assert.equal(next.suppressedSinceLast, 1); assert.equal(next.requestId, 'req-3'); }); test('shadow reporter ignores aligned requests', () => { const emitted = []; const reporter = createPortalAccessShadowReporter({ emit: (event) => emitted.push(event), }); assert.equal(reporter.record({ mismatch: false }), null); assert.deepEqual(reporter.snapshot(), []); assert.deepEqual(emitted, []); });