diff --git a/src/routes/(token-based)/+layout.server.ts b/src/routes/(token-based)/+layout.server.ts
index 543cbb2..ea7d7f9 100644
--- a/src/routes/(token-based)/+layout.server.ts
+++ b/src/routes/(token-based)/+layout.server.ts
@@ -1,23 +1,22 @@
-import {
- vorgangPINValidation,
- vorgangExists
-} from '$lib/server/vorgangService';
+import { vorgangPINValidation, vorgangExists } from '$lib/server/vorgangService';
import { redirect } from '@sveltejs/kit';
-import type { PageServerLoad } from './list/[vorgang]/$types';
+import type { LayoutServerLoad } from './$types';
import { ROUTE_NAMES } from '..';
-export const load: PageServerLoad = async ({ params, url, locals }) => {
+export const load: LayoutServerLoad = async ({ params, cookies, locals }) => {
if (locals.user) {
return {
user: locals.user
};
}
- const vorgangToken = params.vorgang;
- const vorgangPIN = url.searchParams.get('pin');
+ const vorgangToken = params.vorgang || '';
+ const COOKIE_NAME = `token-${vorgangToken}`;
+ const vorgangPIN = cookies.get(COOKIE_NAME) || '';
const isVorgangValid = vorgangExists(vorgangToken);
const isVorgangPINValid = vorgangPINValidation(vorgangToken, vorgangPIN);
- if (!isVorgangValid || !isVorgangPINValid) throw redirect(303, ROUTE_NAMES.ANMELDUNG_VORGANG_PARAM(vorgangToken));
+ if (!isVorgangValid || !isVorgangPINValid)
+ throw redirect(303, ROUTE_NAMES.ANMELDUNG_VORGANG_PARAM(vorgangToken));
};
diff --git a/src/routes/anmeldung/+page.server.ts b/src/routes/anmeldung/+page.server.ts
index bfe8d21..a4d6007 100644
--- a/src/routes/anmeldung/+page.server.ts
+++ b/src/routes/anmeldung/+page.server.ts
@@ -1,3 +1,4 @@
+import { dev } from '$app/environment';
import { loginUser, logoutUser } from '$lib/server/authService';
import { redirect } from '@sveltejs/kit';
import { ROUTE_NAMES } from '../index.js';
@@ -5,13 +6,21 @@ import { ROUTE_NAMES } from '../index.js';
export const actions = {
login: ({ request, cookies }) => loginUser({ request, cookies }),
logout: (event) => logoutUser(event),
- getVorgangByToken: async ({ request }) => {
+ getVorgangByToken: async ({ request, cookies }) => {
const data = await request.formData();
const vorgangToken = data.get('vorgang-token');
const vorgangPIN = data.get('vorgang-pin');
if (!vorgangToken || !vorgangPIN) return;
- throw redirect(303, ROUTE_NAMES.VORGANG(vorgangToken, vorgangPIN));
+ const COOKIE_NAME = `token-${vorgangToken}`
+ cookies.set(COOKIE_NAME, vorgangPIN, {
+ path: '/',
+ httpOnly: true,
+ sameSite: 'strict',
+ secure: !dev
+ });
+
+ throw redirect(303, ROUTE_NAMES.VORGANG(vorgangToken));
}
} as const;
diff --git a/src/routes/index.ts b/src/routes/index.ts
index 176a558..27df54c 100644
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -11,14 +11,8 @@ export const ROUTE_NAMES = {
USERMGMT: '/user-management',
// (token-based)
- // `pin` param is optional
- VORGANG: (vorgangToken: string, vorgangPIN: string) =>
- vorgangPIN ? `/list/${vorgangToken}?pin=${vorgangPIN}` : `/list/${vorgangToken}`,
-
- CRIME: (vorgangToken: string, tatort: string, vorgangPIN: string) =>
- vorgangPIN
- ? `/view/${vorgangToken}/${tatort}?pin=${vorgangPIN}`
- : `/view/${vorgangToken}/${tatort}`,
+ VORGANG: (vorgangToken: string) => `/list/${vorgangToken}`,
+ CRIME: (vorgangToken: string, tatort: string) => `/view/${vorgangToken}/${tatort}`,
// Anmeldung: actions
ANMELDUNG: '/anmeldung',
diff --git a/tests/views/Anmeldung.test.ts b/tests/views/Anmeldung.test.ts
new file mode 100644
index 0000000..96cf9b3
--- /dev/null
+++ b/tests/views/Anmeldung.test.ts
@@ -0,0 +1,144 @@
+import { describe, it, expect, vi } from 'vitest';
+import { actions } from '$root/routes/anmeldung/+page.server';
+import { load } from '$root/routes/(token-based)/+layout.server'
+
+import { baseData } from '../fixtures';
+import { ROUTE_NAMES } from '../../src/routes';
+import { dev } from '$app/environment';
+import { vorgangExists, vorgangPINValidation } from '$lib/server/vorgangService';
+import { Redirect } from '@sveltejs/kit';
+
+vi.mock('$lib/server/vorgangService', () => ({
+ vorgangExists: vi.fn(),
+ vorgangPINValidation: vi.fn(),
+}));
+
+describe('Vorgang Anzeige via Token', () => {
+ it('Setze Cookie nach erfolgreicher Eingabe', async () => {
+ // Mock formData
+ const vorgObj = baseData.vorgang;
+
+ const formData = new FormData();
+ formData.set('vorgang-token', vorgObj.vorgangToken);
+ formData.set('vorgang-pin', vorgObj.vorgangPIN);
+
+ const mockRequest = {
+ formData: vi.fn().mockResolvedValue(formData)
+ };
+
+ const cookiesSet = vi.fn();
+
+ const event = {
+ request: mockRequest,
+ cookies: {
+ set: cookiesSet
+ }
+ };
+
+ let thrownRedirect: Redirect | undefined;
+ try {
+ await actions.getVorgangByToken(event);
+ } catch (e) {
+ thrownRedirect = e as Redirect;
+ }
+
+ // Redirect bei erfolgreicher Eingabe
+ expect(thrownRedirect?.status).toBe(303);
+ expect(thrownRedirect?.location).toBe(ROUTE_NAMES.VORGANG(vorgObj.vorgangToken));
+
+ // Cookie wurde gesetzt
+ const COOKIE_NAME = `token-${vorgObj.vorgangToken}`
+ expect(cookiesSet).toHaveBeenCalledWith(COOKIE_NAME, vorgObj.vorgangPIN, {
+ path: '/',
+ httpOnly: true,
+ sameSite: 'strict',
+ secure: !dev
+ });
+ });
+
+ it('Schlägt fehl wenn keine Daten übergeben werden', async () => {
+ const formData = new FormData(); // no data
+
+ const mockRequest = {
+ formData: vi.fn().mockResolvedValue(formData)
+ };
+
+ const cookiesSet = vi.fn();
+
+ const event = {
+ request: mockRequest,
+ cookies: {
+ set: cookiesSet
+ }
+ };
+
+ const result = await actions.getVorgangByToken(event);
+
+ expect(result).toBeUndefined();
+
+ // Cookie wird nicht gesetzt
+ expect(cookiesSet).not.toHaveBeenCalled();
+ });
+});
+
+describe('Teste Guard', () => {
+ it('Lese Cookie aus', async () => {
+ const vorgObj = baseData.vorgang;
+
+ const COOKIE_NAME = `token-${vorgObj.vorgangToken}`
+ const cookiesGet = vi.fn().mockImplementation((key: string) => {
+ if (key === COOKIE_NAME) return vorgObj.vorgangPIN;
+ return undefined;
+ });
+
+
+ // mocked objects
+ const event = {
+ cookies: {
+ get: cookiesGet
+ },
+ locals: {},
+ params: {vorgang: vorgObj.vorgangToken}
+ };
+ vi.mocked(vorgangExists).mockReturnValueOnce(true);
+ vi.mocked(vorgangPINValidation).mockReturnValueOnce(true);
+
+ await load(event);
+
+ expect(cookiesGet).toHaveBeenCalledWith(COOKIE_NAME);
+ });
+
+ it('Kein Cookie gesetzt', async () => {
+ const vorgObj = baseData.vorgang;
+
+ const COOKIE_NAME = `token-${vorgObj.vorgangToken}`
+ const cookiesGet = vi.fn().mockImplementation((key: string) => {
+ if (key === COOKIE_NAME) return vorgObj.vorgangPIN;
+ return undefined;
+ });
+
+
+ // mocked objects
+ const event = {
+ cookies: {
+ get: cookiesGet
+ },
+ locals: {},
+ params: {vorgang: vorgObj.vorgangToken}
+ };
+ vi.mocked(vorgangExists).mockReturnValueOnce(true);
+ vi.mocked(vorgangPINValidation).mockReturnValueOnce(false);
+
+ let thrownRedirect;
+ try {
+ await load(event);
+ throw new Error('Function did not throw')
+ } catch (e) {
+ thrownRedirect = e;
+ }
+ expect(thrownRedirect?.status).toBe(303);
+ expect(thrownRedirect?.location).toBe(ROUTE_NAMES.ANMELDUNG_VORGANG_PARAM(vorgObj.vorgangToken));
+
+ expect(cookiesGet).toHaveBeenCalledWith(COOKIE_NAME);
+ });
+});
diff --git a/tests/views/VorgangList.view.test.ts b/tests/views/VorgangList.view.test.ts
index c03bdf3..8349c2f 100644
--- a/tests/views/VorgangList.view.test.ts
+++ b/tests/views/VorgangList.view.test.ts
@@ -33,4 +33,13 @@ describe('Teste Links auf Korrektheit', () => {
expect(linkElement).toBeInTheDocument();
expect(linkElement).toHaveAttribute('href', expectedURL);
});
+
+ it('Links enthalten keinen VorgangsPIN', () => {
+ const vorgListOneItem = baseData.vorgangList.slice(0, 1)
+
+ render(VorgangListPage, { props: { data: { ...baseData, vorgangList: vorgListOneItem } } });
+ const listItem = screen.getByTestId("test-list-item");
+ const linkElement = within(listItem).getByRole('link');
+ expect(linkElement.getAttribute('href')?.toLowerCase()).not.toContain('pin');
+ });
});
diff --git a/vite.config.ts b/vite.config.ts
index c506829..ac7ee3b 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -8,7 +8,7 @@ export default defineConfig({
resolve: {
alias: {
$lib: path.resolve('./src/lib'),
- $root: path.resolve(__dirname, 'src')
+ $root: path.resolve(__dirname, './src')
}
},
test: {