d7fb7b7a7b
Build & Deploy / Build & Push Docker Image (push) Failing after 47s
Build & Deploy / Deploy to VPS (push) Has been skipped
Test / API Unit Tests (push) Failing after 5m4s
Test / Marketplace Unit Tests (push) Failing after 4m55s
Test / Admin Unit Tests (push) Successful in 9m37s
Test / Dashboard Unit Tests (push) Successful in 9m37s
Test / API Integration Tests (push) Successful in 9m54s
153 lines
5.7 KiB
TypeScript
153 lines
5.7 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import type { NextFunction, Request, Response } from 'express'
|
|
|
|
vi.mock('jsonwebtoken', () => ({
|
|
default: { verify: vi.fn() },
|
|
}))
|
|
|
|
vi.mock('../lib/prisma', () => ({
|
|
prisma: {
|
|
adminUser: { findUnique: vi.fn() },
|
|
},
|
|
}))
|
|
|
|
import jwt from 'jsonwebtoken'
|
|
import { prisma } from '../lib/prisma'
|
|
import { requireAdminAuth, requireAdminRole } from './requireAdminAuth'
|
|
|
|
function responseStub() {
|
|
const res = { status: vi.fn(), json: vi.fn() }
|
|
res.status.mockReturnValue(res)
|
|
res.json.mockReturnValue(res)
|
|
return res as unknown as Response & typeof res
|
|
}
|
|
|
|
describe('requireAdminAuth middleware', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
process.env.JWT_SECRET = 'test-secret'
|
|
})
|
|
|
|
it('rejects requests without an admin bearer token', async () => {
|
|
const req = { headers: {} } as Request
|
|
const res = responseStub()
|
|
const next = vi.fn() as NextFunction
|
|
|
|
await requireAdminAuth(req, res, next)
|
|
|
|
expect(res.status).toHaveBeenCalledWith(401)
|
|
expect(res.json).toHaveBeenCalledWith({ error: 'unauthenticated', message: 'Admin authentication required', statusCode: 401 })
|
|
expect(next).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('rejects invalid token signatures', async () => {
|
|
vi.mocked(jwt.verify).mockImplementation(() => { throw new Error('bad token') })
|
|
const req = { headers: { authorization: 'Bearer bad' } } as Request
|
|
const res = responseStub()
|
|
const next = vi.fn() as NextFunction
|
|
|
|
await requireAdminAuth(req, res, next)
|
|
|
|
expect(res.status).toHaveBeenCalledWith(401)
|
|
expect(res.json).toHaveBeenCalledWith({ error: 'invalid_token', message: 'Invalid or expired admin token', statusCode: 401 })
|
|
expect(prisma.adminUser.findUnique).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('rejects non-admin token types', async () => {
|
|
vi.mocked(jwt.verify).mockReturnValue({ sub: 'emp_1', type: 'employee' } as any)
|
|
const req = { headers: { authorization: 'Bearer employee-token' } } as Request
|
|
const res = responseStub()
|
|
const next = vi.fn() as NextFunction
|
|
|
|
await requireAdminAuth(req, res, next)
|
|
|
|
expect(res.status).toHaveBeenCalledWith(401)
|
|
expect(res.json).toHaveBeenCalledWith({ error: 'invalid_token', message: 'Invalid or expired admin token', statusCode: 401 })
|
|
expect(prisma.adminUser.findUnique).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('rejects inactive admin accounts', async () => {
|
|
vi.mocked(jwt.verify).mockReturnValue({ sub: 'admin_1', type: 'admin' } as any)
|
|
vi.mocked(prisma.adminUser.findUnique).mockResolvedValue({ id: 'admin_1', isActive: false } as any)
|
|
const req = { headers: { authorization: 'Bearer admin-token' } } as Request
|
|
const res = responseStub()
|
|
const next = vi.fn() as NextFunction
|
|
|
|
await requireAdminAuth(req, res, next)
|
|
|
|
expect(res.status).toHaveBeenCalledWith(401)
|
|
expect(res.json).toHaveBeenCalledWith({ error: 'unauthenticated', message: 'Admin account not found or deactivated', statusCode: 401 })
|
|
expect(next).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('attaches the admin record for valid admin tokens', async () => {
|
|
const admin = { id: 'admin_1', isActive: true, role: 'SUPPORT', totpEnabled: true }
|
|
vi.mocked(jwt.verify).mockReturnValue({ sub: 'admin_1', type: 'admin' } as any)
|
|
vi.mocked(prisma.adminUser.findUnique).mockResolvedValue(admin as any)
|
|
const req = { headers: { authorization: 'Bearer admin-token' } } as Request
|
|
const res = responseStub()
|
|
const next = vi.fn() as NextFunction
|
|
|
|
await requireAdminAuth(req, res, next)
|
|
|
|
expect(prisma.adminUser.findUnique).toHaveBeenCalledWith({ where: { id: 'admin_1' } })
|
|
expect(req.admin).toEqual(admin)
|
|
expect(next).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('blocks non-enrolled admins from privileged routes', async () => {
|
|
vi.mocked(jwt.verify).mockReturnValue({ sub: 'admin_1', type: 'admin' } as any)
|
|
vi.mocked(prisma.adminUser.findUnique).mockResolvedValue({ id: 'admin_1', isActive: true, role: 'ADMIN', totpEnabled: false } as any)
|
|
const req = { headers: { authorization: 'Bearer admin-token' }, path: '/companies' } as Request
|
|
const res = responseStub()
|
|
const next = vi.fn() as NextFunction
|
|
|
|
await requireAdminAuth(req, res, next)
|
|
|
|
expect(res.status).toHaveBeenCalledWith(403)
|
|
expect(res.json).toHaveBeenCalledWith({ error: 'admin_2fa_required', message: 'Admin 2FA enrollment is required before using privileged admin routes', statusCode: 403 })
|
|
expect(next).not.toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('requireAdminRole middleware', () => {
|
|
it('requires requireAdminAuth to run first', () => {
|
|
const req = {} as Request
|
|
const res = responseStub()
|
|
const next = vi.fn() as NextFunction
|
|
|
|
requireAdminRole('SUPPORT' as any)(req, res, next)
|
|
|
|
expect(res.status).toHaveBeenCalledWith(401)
|
|
expect(res.json).toHaveBeenCalledWith({ error: 'unauthenticated', message: 'Admin authentication required', statusCode: 401 })
|
|
expect(next).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('blocks admins below the required rank', () => {
|
|
const req = { admin: { role: 'VIEWER' } } as Request
|
|
const res = responseStub()
|
|
const next = vi.fn() as NextFunction
|
|
|
|
requireAdminRole('FINANCE' as any)(req, res, next)
|
|
|
|
expect(res.status).toHaveBeenCalledWith(403)
|
|
expect(res.json).toHaveBeenCalledWith({
|
|
error: 'forbidden',
|
|
message: 'This action requires the FINANCE role or higher',
|
|
statusCode: 403,
|
|
})
|
|
expect(next).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('allows admins at or above the required rank', () => {
|
|
const req = { admin: { role: 'ADMIN' } } as Request
|
|
const res = responseStub()
|
|
const next = vi.fn() as NextFunction
|
|
|
|
requireAdminRole('SUPPORT' as any)(req, res, next)
|
|
|
|
expect(next).toHaveBeenCalledTimes(1)
|
|
expect(res.status).not.toHaveBeenCalled()
|
|
})
|
|
})
|