release: v0.19.0
Release v0.19.0. See the `## [0.19.0] — 2026-09-29` section of [CHANGELOG.md](https://github.com/ralksta/immich-folio/blob/dev/CHANGELOG.md) for the full list. Highlights: client proofing links (#683), custom content pages (#738), asset picker search (#740), concurrent-edit detection (#742), env-locked settings (#739), an accessibility baseline (#741), three security fixes with advisories (#737, #755, #759), and a pre-release quality pass (#780–#797). --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: ralksta <ralksta@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Ralf <rohlenbo79@gmail.com> Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com> Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com> Co-authored-by: Lance Martin <lancetaylor95@gmail.com>
This commit is contained in:
71
lib/__tests__/auth-page.test.ts
Normal file
71
lib/__tests__/auth-page.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
/**
|
||||
* Content page passwords (#722): their own cookie, `lb_auth_page_<slug>`,
|
||||
* through the same authenticate()/isAuthenticated() path as every other gate,
|
||||
* and a token that no other gate of the same slug and password accepts.
|
||||
*/
|
||||
vi.mock('@/lib/config', () => ({
|
||||
getConfig: () => ({
|
||||
authSecret: 'test-auth-secret-32-chars-long-min',
|
||||
standaloneAlbums: [],
|
||||
albumPasswords: {},
|
||||
subpages: [{ name: 'Pricing', slug: 'pricing', albumIds: [], password: 'shared' }],
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/admin/pages-service', () => ({
|
||||
readPageSync: (slug: string) =>
|
||||
slug === 'pricing' || slug === 'open'
|
||||
? {
|
||||
slug,
|
||||
rawMarkdown: '',
|
||||
parsed: {
|
||||
frontmatter: slug === 'pricing' ? { password: 'shared' } : {},
|
||||
blocks: [],
|
||||
referencedAssetIds: [],
|
||||
},
|
||||
}
|
||||
: null,
|
||||
}));
|
||||
|
||||
vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
import { authenticate, isAuthenticated, isProtected } from '@/lib/auth';
|
||||
|
||||
function cookieFrom(setCookie: string): [string, string] {
|
||||
const [pair] = setCookie.split(';');
|
||||
const eq = pair.indexOf('=');
|
||||
return [pair.slice(0, eq), pair.slice(eq + 1)];
|
||||
}
|
||||
|
||||
describe('content page passwords', () => {
|
||||
it('knows which pages are protected', () => {
|
||||
expect(isProtected('pricing', 'page')).toBe(true);
|
||||
expect(isProtected('open', 'page')).toBe(false);
|
||||
expect(isProtected('missing', 'page')).toBe(false);
|
||||
});
|
||||
|
||||
it('sets lb_auth_page_<slug> and accepts it back', async () => {
|
||||
const setCookie = await authenticate('pricing', 'shared', 'page');
|
||||
expect(setCookie).not.toBeNull();
|
||||
const [name, value] = cookieFrom(setCookie!);
|
||||
expect(name).toBe('lb_auth_page_pricing');
|
||||
expect(isAuthenticated('pricing', (n) => (n === name ? value : undefined), 'page')).toBe(true);
|
||||
});
|
||||
|
||||
it('refuses a wrong password', async () => {
|
||||
expect(await authenticate('pricing', 'nope', 'page')).toBeNull();
|
||||
});
|
||||
|
||||
it('does not accept a subpage token of the same slug and password', async () => {
|
||||
const [, subpageValue] = cookieFrom((await authenticate('pricing', 'shared', 'subpage'))!);
|
||||
expect(
|
||||
isAuthenticated(
|
||||
'pricing',
|
||||
(n) => (n === 'lb_auth_page_pricing' ? subpageValue : undefined),
|
||||
'page',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user