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:
114
frontend/src/routes/admin/EntityNav.svelte
Normal file
114
frontend/src/routes/admin/EntityNav.svelte
Normal file
@@ -0,0 +1,114 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
|
||||
let {
|
||||
userCount,
|
||||
groupCount,
|
||||
tagCount,
|
||||
canManageUsers,
|
||||
canManageTags,
|
||||
canManageGroups,
|
||||
canRunMaintenance
|
||||
}: {
|
||||
userCount: number;
|
||||
groupCount: number;
|
||||
tagCount: number;
|
||||
canManageUsers: boolean;
|
||||
canManageTags: boolean;
|
||||
canManageGroups: boolean;
|
||||
canRunMaintenance: boolean;
|
||||
} = $props();
|
||||
|
||||
const currentPath = $derived(page.url.pathname);
|
||||
const isActive = (section: string) => currentPath.startsWith(`/admin/${section}`);
|
||||
</script>
|
||||
|
||||
<nav class="flex w-30 flex-shrink-0 flex-col bg-brand-navy" aria-label={m.admin_heading()}>
|
||||
<div class="px-3 pt-3 pb-1 text-[9px] font-extrabold tracking-widest text-white/30 uppercase">
|
||||
{m.admin_heading()}
|
||||
</div>
|
||||
|
||||
{#if canManageUsers}
|
||||
<a
|
||||
href="/admin/users"
|
||||
class="flex flex-col gap-0.5 border-l-[3px] px-3.5 py-2.5 transition-colors
|
||||
{isActive('users')
|
||||
? 'border-brand-mint bg-white/10'
|
||||
: 'border-transparent hover:bg-white/5'}"
|
||||
aria-current={isActive('users') ? 'page' : undefined}
|
||||
>
|
||||
<span class="text-[13px] font-black {isActive('users') ? 'text-white/65' : 'text-white/20'}">
|
||||
{userCount}
|
||||
</span>
|
||||
<span
|
||||
class="text-[9px] font-extrabold tracking-[0.5px] uppercase
|
||||
{isActive('users') ? 'text-white' : 'text-white/55'}"
|
||||
>
|
||||
{m.admin_tab_users()}
|
||||
</span>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
{#if canManageGroups}
|
||||
<a
|
||||
href="/admin/groups"
|
||||
class="flex flex-col gap-0.5 border-l-[3px] px-3.5 py-2.5 transition-colors
|
||||
{isActive('groups')
|
||||
? 'border-brand-mint bg-white/10'
|
||||
: 'border-transparent hover:bg-white/5'}"
|
||||
aria-current={isActive('groups') ? 'page' : undefined}
|
||||
>
|
||||
<span class="text-[13px] font-black {isActive('groups') ? 'text-white/65' : 'text-white/20'}">
|
||||
{groupCount}
|
||||
</span>
|
||||
<span
|
||||
class="text-[9px] font-extrabold tracking-[0.5px] uppercase
|
||||
{isActive('groups') ? 'text-white' : 'text-white/55'}"
|
||||
>
|
||||
{m.admin_tab_groups()}
|
||||
</span>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
{#if canManageTags}
|
||||
<a
|
||||
href="/admin/tags"
|
||||
class="flex flex-col gap-0.5 border-l-[3px] px-3.5 py-2.5 transition-colors
|
||||
{isActive('tags')
|
||||
? 'border-brand-mint bg-white/10'
|
||||
: 'border-transparent hover:bg-white/5'}"
|
||||
aria-current={isActive('tags') ? 'page' : undefined}
|
||||
>
|
||||
<span class="text-[13px] font-black {isActive('tags') ? 'text-white/65' : 'text-white/20'}">
|
||||
{tagCount}
|
||||
</span>
|
||||
<span
|
||||
class="text-[9px] font-extrabold tracking-[0.5px] uppercase
|
||||
{isActive('tags') ? 'text-white' : 'text-white/55'}"
|
||||
>
|
||||
{m.admin_tab_tags()}
|
||||
</span>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
<div class="flex-1"></div>
|
||||
|
||||
{#if canRunMaintenance}
|
||||
<a
|
||||
href="/admin/system"
|
||||
class="flex flex-col gap-0.5 border-t border-l-[3px] border-white/10 px-3.5 py-2.5 transition-colors
|
||||
{isActive('system')
|
||||
? 'border-brand-mint bg-white/10'
|
||||
: 'border-l-transparent hover:bg-white/5'}"
|
||||
aria-current={isActive('system') ? 'page' : undefined}
|
||||
>
|
||||
<span
|
||||
class="text-[9px] font-extrabold tracking-[0.5px] uppercase
|
||||
{isActive('system') ? 'text-white' : 'text-white/55'}"
|
||||
>
|
||||
{m.admin_tab_system()}
|
||||
</span>
|
||||
</a>
|
||||
{/if}
|
||||
</nav>
|
||||
Reference in New Issue
Block a user