feat(ST-nma6ylbl): requireAdmin() guard + /admin layout-shell + tests
- lib/auth-guard.ts: requireAdmin() toegevoegd — redirect /dashboard bij !userId of !isAdmin - app/(app)/admin/layout.tsx: admin-sidebar met links naar /admin/users, /admin/jobs, /admin/products - app/(app)/admin/page.tsx: redirect-stub naar /admin/users - __tests__/lib/auth-guard.test.ts: 3 tests voor requireAdmin() (geen userId, isAdmin=false, isAdmin=true)
This commit is contained in:
parent
8af5354f22
commit
c0ded1f482
4 changed files with 64 additions and 0 deletions
|
|
@ -8,6 +8,41 @@ vi.mock('@/lib/auth', () => ({ getSession: getSessionMock }))
|
||||||
vi.mock('@/lib/auth/pairing', () => ({ isPairedSessionExpired: isPairedSessionExpiredMock }))
|
vi.mock('@/lib/auth/pairing', () => ({ isPairedSessionExpired: isPairedSessionExpiredMock }))
|
||||||
vi.mock('next/navigation', () => ({ redirect: redirectMock }))
|
vi.mock('next/navigation', () => ({ redirect: redirectMock }))
|
||||||
|
|
||||||
|
describe('requireAdmin', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
getSessionMock.mockReset()
|
||||||
|
isPairedSessionExpiredMock.mockReset()
|
||||||
|
redirectMock.mockClear()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.resetModules()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('redirect /dashboard als userId ontbreekt', async () => {
|
||||||
|
getSessionMock.mockResolvedValue({ userId: undefined, isAdmin: false })
|
||||||
|
const { requireAdmin } = await import('@/lib/auth-guard')
|
||||||
|
await expect(requireAdmin()).rejects.toThrow('REDIRECT_CALLED')
|
||||||
|
expect(redirectMock).toHaveBeenCalledWith('/dashboard')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('redirect /dashboard als isAdmin false is', async () => {
|
||||||
|
getSessionMock.mockResolvedValue({ userId: 'u1', isAdmin: false })
|
||||||
|
const { requireAdmin } = await import('@/lib/auth-guard')
|
||||||
|
await expect(requireAdmin()).rejects.toThrow('REDIRECT_CALLED')
|
||||||
|
expect(redirectMock).toHaveBeenCalledWith('/dashboard')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('geeft sessie terug als isAdmin true is', async () => {
|
||||||
|
const sess = { userId: 'u1', isAdmin: true }
|
||||||
|
getSessionMock.mockResolvedValue(sess)
|
||||||
|
const { requireAdmin } = await import('@/lib/auth-guard')
|
||||||
|
const result = await requireAdmin()
|
||||||
|
expect(result).toBe(sess)
|
||||||
|
expect(redirectMock).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('requireSession', () => {
|
describe('requireSession', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
getSessionMock.mockReset()
|
getSessionMock.mockReset()
|
||||||
|
|
|
||||||
16
app/(app)/admin/layout.tsx
Normal file
16
app/(app)/admin/layout.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { requireAdmin } from '@/lib/auth-guard'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
export default async function AdminLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
await requireAdmin()
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen">
|
||||||
|
<nav className="w-48 border-r p-4 flex flex-col gap-2">
|
||||||
|
<Link href="/admin/users">Gebruikers</Link>
|
||||||
|
<Link href="/admin/jobs">Claude Jobs</Link>
|
||||||
|
<Link href="/admin/products">Producten</Link>
|
||||||
|
</nav>
|
||||||
|
<main className="flex-1 p-6">{children}</main>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
5
app/(app)/admin/page.tsx
Normal file
5
app/(app)/admin/page.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { redirect } from 'next/navigation'
|
||||||
|
|
||||||
|
export default function AdminPage() {
|
||||||
|
redirect('/admin/users')
|
||||||
|
}
|
||||||
|
|
@ -22,3 +22,11 @@ export async function requireSession() {
|
||||||
|
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function requireAdmin() {
|
||||||
|
const session = await getSession()
|
||||||
|
if (!session.userId || !session.isAdmin) {
|
||||||
|
redirect('/dashboard')
|
||||||
|
}
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue