Some checks failed
PR Checks / lint-test-and-docker-build (pull_request) Failing after 23s
85 lines
3.0 KiB
TypeScript
85 lines
3.0 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { setupTestDb } from '../../tests/helpers.js';
|
|
import type { Database } from './db/types.js';
|
|
import { setLocalCredentialPassword } from './local-credentials.js';
|
|
import { resetLocalAuthRateLimits, verifyLocalCredentials } from './local-auth.js';
|
|
import { upsertUserFromAuth } from './users.js';
|
|
|
|
let database: Database;
|
|
|
|
beforeEach(() => {
|
|
process.env.LOCAL_AUTH_ENABLED = 'true';
|
|
process.env.LOCAL_AUTH_ARGON2_MEMORY_KB = '8192';
|
|
process.env.LOCAL_AUTH_ARGON2_TIME_COST = '2';
|
|
process.env.LOCAL_AUTH_ARGON2_PARALLELISM = '1';
|
|
process.env.LOCAL_AUTH_MAX_ATTEMPTS = '2';
|
|
process.env.LOCAL_AUTH_WINDOW_SECONDS = '60';
|
|
process.env.LOCAL_AUTH_LOCKOUT_SECONDS = '10';
|
|
database = setupTestDb();
|
|
resetLocalAuthRateLimits();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
resetLocalAuthRateLimits();
|
|
database.close();
|
|
});
|
|
|
|
describe('verifyLocalCredentials', () => {
|
|
it('authenticates valid local credentials', async () => {
|
|
upsertUserFromAuth({
|
|
id: 'user-1',
|
|
username: 'jdoe',
|
|
fullName: 'Jane Doe',
|
|
email: 'jane@example.com',
|
|
authSource: 'Local'
|
|
});
|
|
await setLocalCredentialPassword('user-1', 'averysecurepassword');
|
|
|
|
const result = await verifyLocalCredentials('jdoe', 'averysecurepassword', '127.0.0.1');
|
|
|
|
expect(result.status).toBe('success');
|
|
if (result.status === 'success') {
|
|
expect(result.user.id).toBe('user-1');
|
|
expect(result.user.email).toBe('jane@example.com');
|
|
}
|
|
});
|
|
|
|
it('returns invalid for bad credentials and locks after max attempts', async () => {
|
|
upsertUserFromAuth({ id: 'user-2', username: 'sally', fullName: 'Sally Sample' });
|
|
await setLocalCredentialPassword('user-2', 'averysecurepassword');
|
|
|
|
const first = await verifyLocalCredentials('sally', 'wrong-password', '10.0.0.1');
|
|
const second = await verifyLocalCredentials('sally', 'wrong-password', '10.0.0.1');
|
|
const third = await verifyLocalCredentials('sally', 'wrong-password', '10.0.0.1');
|
|
|
|
expect(first.status).toBe('invalid');
|
|
expect(second.status).toBe('invalid');
|
|
expect(third.status).toBe('locked');
|
|
});
|
|
|
|
it('clears lockout after window expires', async () => {
|
|
upsertUserFromAuth({ id: 'user-3', username: 'morgan', fullName: 'Morgan West' });
|
|
await setLocalCredentialPassword('user-3', 'averysecurepassword');
|
|
vi.useFakeTimers();
|
|
vi.setSystemTime(new Date('2024-01-01T00:00:00Z'));
|
|
|
|
await verifyLocalCredentials('morgan', 'wrong-password', '10.0.0.2');
|
|
await verifyLocalCredentials('morgan', 'wrong-password', '10.0.0.2');
|
|
const locked = await verifyLocalCredentials('morgan', 'averysecurepassword', '10.0.0.2');
|
|
|
|
expect(locked.status).toBe('locked');
|
|
|
|
vi.advanceTimersByTime(11_000);
|
|
const after = await verifyLocalCredentials('morgan', 'averysecurepassword', '10.0.0.2');
|
|
|
|
expect(after.status).toBe('success');
|
|
});
|
|
|
|
it('rejects when local auth is disabled', async () => {
|
|
process.env.LOCAL_AUTH_ENABLED = 'false';
|
|
const result = await verifyLocalCredentials('anyone', 'password', '10.0.0.3');
|
|
expect(result.status).toBe('disabled');
|
|
});
|
|
});
|