feat(portal): reuse unlock code for protected downloads
This commit is contained in:
1 parent
475dd3379b
commit
596246f20a
9 files changed
+268
-13
No files matched your search
@@ -10,6 +10,13 @@ import { decryptPortalStream, encryptPortalStream, validatePortalPayload, portal
|
||||
import { collectResource, resourceReferences, rewriteResources } from './portal-resources.mjs';
|
||||
import { isFormACompletion } from '../src/utils/jotform-completion.ts';
|
||||
import { portalLinkPresentation } from '../src/utils/portal-presentation.ts';
|
||||
import {
|
||||
PortalDownloadError,
|
||||
fetchProtectedPortalResource,
|
||||
portalDownloadErrorMessage,
|
||||
portalRoleForStreamId,
|
||||
protectedPortalResource,
|
||||
} from '../src/utils/portal-download.mjs';
|
||||
import { applyRoleMappings, approvedMapping, summarizeMappings } from './portal-production.mjs';
|
||||
|
||||
test('completion envelope requires an exact approved origin, active frame window and string Form A ID', () => {
|
||||
@@ -48,6 +55,59 @@ test('portal presentation translates only explicit button tokens/brand fills and
|
||||
assert.equal(portalLinkPresentation([], '', 'tel:'), 'portal-contact-link');
|
||||
});
|
||||
|
||||
test('portal downloads use only the validated role and an in-memory authorization header', async () => {
|
||||
assert.equal(portalRoleForStreamId('b'), 'staff');
|
||||
assert.equal(portalRoleForStreamId('a'), 'management');
|
||||
assert.equal(portalRoleForStreamId('fixture-staff'), undefined);
|
||||
const origin = 'https://www.azinstitute4autism.com';
|
||||
const staff = protectedPortalResource('/staff-resources/staff-guide.pdf', origin);
|
||||
const management = protectedPortalResource('/management-resources/management-guide.pdf', origin);
|
||||
assert.deepEqual(staff, {
|
||||
role: 'staff', filename: 'staff-guide.pdf',
|
||||
url: origin + '/staff-resources/staff-guide.pdf',
|
||||
});
|
||||
assert.equal(protectedPortalResource('/staff-resources/nested/file.pdf', origin), undefined);
|
||||
assert.equal(protectedPortalResource('/staff-resources/file.pdf?code=forbidden', origin), undefined);
|
||||
assert.equal(protectedPortalResource('https://other.invalid/staff-resources/file.pdf', origin), undefined);
|
||||
|
||||
const code = 'synthetic browser memory only';
|
||||
let captured;
|
||||
const result = await fetchProtectedPortalResource(staff, { role: 'staff', code }, async (url, options) => {
|
||||
captured = { url, options };
|
||||
return new Response('synthetic document', {
|
||||
headers: { 'content-disposition': 'attachment; filename="approved-name.pdf"' },
|
||||
});
|
||||
});
|
||||
assert.equal(captured.url, staff.url);
|
||||
assert.equal(captured.url.includes(code), false);
|
||||
assert.equal(captured.options.method, 'GET');
|
||||
assert.equal(captured.options.credentials, 'omit');
|
||||
assert.equal(captured.options.cache, 'no-store');
|
||||
assert.equal(captured.options.redirect, 'error');
|
||||
assert.equal(captured.options.referrerPolicy, 'no-referrer');
|
||||
assert.match(captured.options.headers.Authorization, /^Basic /);
|
||||
assert.equal(result.filename, 'approved-name.pdf');
|
||||
assert.equal(await result.blob.text(), 'synthetic document');
|
||||
|
||||
for (const [resource, credential] of [
|
||||
[management, { role: 'staff', code }],
|
||||
[staff, { role: 'management', code }],
|
||||
]) {
|
||||
let called = false;
|
||||
await assert.rejects(
|
||||
fetchProtectedPortalResource(resource, credential, async () => { called = true; return new Response(); }),
|
||||
(error) => error instanceof PortalDownloadError && error.code === 'role',
|
||||
);
|
||||
assert.equal(called, false, 'cross-role credentials are never transmitted');
|
||||
}
|
||||
await assert.rejects(
|
||||
fetchProtectedPortalResource(staff, { role: 'staff', code }, async () => new Response('', { status: 401 })),
|
||||
(error) => error instanceof PortalDownloadError && error.code === 'unauthorized',
|
||||
);
|
||||
assert.match(portalDownloadErrorMessage(new PortalDownloadError('unauthorized')), /session could not authorize/);
|
||||
assert.match(portalDownloadErrorMessage(new PortalDownloadError('role')), /not available from this portal/);
|
||||
});
|
||||
|
||||
test('staff template presentation follows sanitized headings, not original HubSpot classes', async () => {
|
||||
const renderer = await fs.readFile('src/scripts/employee-portal.ts', 'utf8');
|
||||
const css = await fs.readFile('src/components/pages/EmployeePortal.astro', 'utf8');
|
||||
|
||||
Reference in new issue
Block a user