fix(admin): guard GET /api/users/{id} with @RequirePermission(ADMIN_USER)
Fixes IDOR: the endpoint was publicly accessible to any authenticated user. Now requires ADMIN_USER permission, matching all other user management endpoints. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
87
frontend/src/routes/admin/layout.svelte.spec.ts
Normal file
87
frontend/src/routes/admin/layout.svelte.spec.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Layout shell tests — we test EntityNav.svelte directly since the layout
|
||||
* itself is a thin shell that just composes EntityNav and renders children.
|
||||
*/
|
||||
import { afterEach, describe, it, expect, vi } from 'vitest';
|
||||
import { cleanup, render } from 'vitest-browser-svelte';
|
||||
import { page } from 'vitest/browser';
|
||||
import EntityNav from './EntityNav.svelte';
|
||||
|
||||
vi.mock('$app/state', () => ({
|
||||
page: { url: { pathname: '/admin/users' } }
|
||||
}));
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
const fullPerms = {
|
||||
userCount: 4,
|
||||
groupCount: 3,
|
||||
tagCount: 7,
|
||||
canManageUsers: true,
|
||||
canManageTags: true,
|
||||
canManageGroups: true,
|
||||
canRunMaintenance: true
|
||||
};
|
||||
|
||||
describe('admin EntityNav — links', () => {
|
||||
it('renders users nav link pointing to /admin/users', async () => {
|
||||
render(EntityNav, fullPerms);
|
||||
await expect
|
||||
.element(page.getByRole('link', { name: /benutzer/i }))
|
||||
.toHaveAttribute('href', '/admin/users');
|
||||
});
|
||||
|
||||
it('renders groups nav link pointing to /admin/groups', async () => {
|
||||
render(EntityNav, fullPerms);
|
||||
await expect
|
||||
.element(page.getByRole('link', { name: /gruppen/i }))
|
||||
.toHaveAttribute('href', '/admin/groups');
|
||||
});
|
||||
|
||||
it('renders tags nav link pointing to /admin/tags', async () => {
|
||||
render(EntityNav, fullPerms);
|
||||
await expect
|
||||
.element(page.getByRole('link', { name: /schlagworte/i }))
|
||||
.toHaveAttribute('href', '/admin/tags');
|
||||
});
|
||||
|
||||
it('renders system nav link pointing to /admin/system', async () => {
|
||||
render(EntityNav, fullPerms);
|
||||
await expect
|
||||
.element(page.getByRole('link', { name: /system/i }))
|
||||
.toHaveAttribute('href', '/admin/system');
|
||||
});
|
||||
});
|
||||
|
||||
describe('admin EntityNav — permission-based rendering', () => {
|
||||
it('hides users link when canManageUsers is false', async () => {
|
||||
render(EntityNav, { ...fullPerms, canManageUsers: false });
|
||||
await expect.element(page.getByRole('link', { name: /benutzer/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides tags link when canManageTags is false', async () => {
|
||||
render(EntityNav, { ...fullPerms, canManageTags: false });
|
||||
await expect.element(page.getByRole('link', { name: /schlagworte/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides system link when canRunMaintenance is false', async () => {
|
||||
render(EntityNav, { ...fullPerms, canRunMaintenance: false });
|
||||
await expect.element(page.getByRole('link', { name: /system/i })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('admin EntityNav — active state', () => {
|
||||
it('marks users link as aria-current=page when on /admin/users', async () => {
|
||||
render(EntityNav, fullPerms);
|
||||
await expect
|
||||
.element(page.getByRole('link', { name: /benutzer/i }))
|
||||
.toHaveAttribute('aria-current', 'page');
|
||||
});
|
||||
|
||||
it('does not mark groups link as current when on /admin/users', async () => {
|
||||
render(EntityNav, fullPerms);
|
||||
await expect
|
||||
.element(page.getByRole('link', { name: /gruppen/i }))
|
||||
.not.toHaveAttribute('aria-current');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user