Project-Image-Uploader/backend/tests/unit/auth.test.js
matthias.lotz 6332b82c6a Feature Request: admin session security
- replace bearer auth with session+CSRF flow and add admin user directory

- update frontend moderation flow, force password change gate, and new CLI

- refresh changelog/docs/feature plan + ensure swagger dev experience
2025-11-23 21:18:42 +01:00

149 lines
4.6 KiB
JavaScript

const { requireAdminAuth } = require('../../src/middlewares/auth');
const AdminAuthService = require('../../src/services/AdminAuthService');
const AdminUserRepository = require('../../src/repositories/AdminUserRepository');
const dbManager = require('../../src/database/DatabaseManager');
describe('Auth Middleware Unit Test (Session based)', () => {
let req, res, next;
beforeEach(() => {
req = { session: null };
res = {
status: jest.fn().mockReturnThis(),
json: jest.fn(),
locals: {}
};
next = jest.fn();
});
test('should reject when no session exists', () => {
requireAdminAuth(req, res, next);
expect(res.status).toHaveBeenCalledWith(403);
expect(res.json).toHaveBeenCalledWith(
expect.objectContaining({
error: 'Zugriff verweigert',
reason: 'SESSION_REQUIRED'
})
);
expect(next).not.toHaveBeenCalled();
});
test('should reject when session user is missing', () => {
req.session = {};
requireAdminAuth(req, res, next);
expect(res.status).toHaveBeenCalledWith(403);
expect(res.json).toHaveBeenCalledWith(
expect.objectContaining({ reason: 'SESSION_REQUIRED' })
);
expect(next).not.toHaveBeenCalled();
});
test('should reject non-admin roles', () => {
req.session = { user: { id: 1, role: 'viewer' } };
requireAdminAuth(req, res, next);
expect(res.status).toHaveBeenCalledWith(403);
expect(res.json).toHaveBeenCalledWith(
expect.objectContaining({ reason: 'SESSION_REQUIRED' })
);
expect(next).not.toHaveBeenCalled();
});
test('should pass through for admin sessions and expose user on locals', () => {
const adminUser = { id: 1, role: 'admin', username: 'testadmin' };
req.session = { user: adminUser };
requireAdminAuth(req, res, next);
expect(next).toHaveBeenCalled();
expect(res.status).not.toHaveBeenCalled();
expect(res.locals.adminUser).toEqual(adminUser);
});
});
describe('AdminAuthService', () => {
beforeEach(async () => {
await dbManager.run('DELETE FROM admin_users');
});
afterEach(async () => {
await dbManager.run('DELETE FROM admin_users');
});
test('needsInitialSetup reflects admin count', async () => {
await expect(AdminAuthService.needsInitialSetup()).resolves.toBe(true);
await AdminAuthService.createInitialAdmin({
username: 'existing',
password: 'SuperSecure123!'
});
await expect(AdminAuthService.needsInitialSetup()).resolves.toBe(false);
});
test('createInitialAdmin validates input and detects completed setup', async () => {
await expect(
AdminAuthService.createInitialAdmin({ username: '', password: 'SuperSecure123!' })
).rejects.toThrow('USERNAME_REQUIRED');
await expect(
AdminAuthService.createInitialAdmin({ username: 'admin', password: 'short' })
).rejects.toThrow('PASSWORD_TOO_WEAK');
await AdminAuthService.createInitialAdmin({ username: 'seed', password: 'SuperSecure123!' });
await expect(
AdminAuthService.createInitialAdmin({ username: 'admin', password: 'SuperSecure123!' })
).rejects.toThrow('SETUP_ALREADY_COMPLETED');
});
test('createInitialAdmin persists normalized admin when setup allowed', async () => {
const result = await AdminAuthService.createInitialAdmin({
username: 'TestAdmin',
password: 'SuperSecure123!'
});
expect(result.username).toBe('testadmin');
expect(result.role).toBe('admin');
const stored = await AdminUserRepository.getByUsername('testadmin');
expect(stored).toMatchObject({ username: 'testadmin', role: 'admin', is_active: 1 });
});
test('verifyCredentials handles missing users and password mismatches', async () => {
await expect(AdminAuthService.verifyCredentials('admin', 'pw')).resolves.toBeNull();
const hash = await AdminAuthService.hashPassword('SuperSecure123!');
await AdminUserRepository.createAdminUser({
username: 'admin',
passwordHash: hash,
role: 'admin',
requiresPasswordChange: false
});
await expect(AdminAuthService.verifyCredentials('admin', 'wrong')).resolves.toBeNull();
});
test('verifyCredentials returns sanitized user for valid credentials', async () => {
const hash = await AdminAuthService.hashPassword('SuperSecure123!');
await AdminUserRepository.createAdminUser({
username: 'admin',
passwordHash: hash,
role: 'admin',
requiresPasswordChange: true
});
const result = await AdminAuthService.verifyCredentials('admin', 'SuperSecure123!');
expect(result).toEqual({
id: expect.any(Number),
username: 'admin',
role: 'admin',
requiresPasswordChange: true
});
});
});