Files
ln-bi/src/server/auth/password.test.ts
T
kfluousandHiFox Agent 0dc7e89c1d
ci/woodpecker/push/woodpecker Pipeline was successful
feat(auth): add configurable password login with SSO default
Co-authored-by: HiFox Agent <agents-noreply@hifox.com>
2026-09-05 18:22:41 +08:00

60 lines
3.2 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { Hono } from 'hono';
import jwt from 'jsonwebtoken';
import { passwordRouter } from './password.js';
import { authMiddleware } from './middleware.js';
import { readOnlyMiddleware } from '../read-only-middleware.js';
import { verifyAuthToken } from './config.js';
test('固定密码模式默认关闭、失败限流、只读权限及换密失效', async () => {
const previous = { ...process.env };
try {
delete process.env.BI_AUTH_MODE;
process.env.JWT_SECRET = 'test-signing-secret-with-at-least-32-characters';
process.env.BI_AUTH_PASSWORD = 'test-password-only-123456';
process.env.DEV_BYPASS_AUTH = '0';
const app = new Hono();
app.use('/api/*', readOnlyMiddleware);
app.route('/api/auth', passwordRouter());
app.use('/api/*', authMiddleware);
app.get('/api/data', c => c.json({ ok: true }));
app.post('/api/data', c => c.json({ ok: true }));
const login = (password: unknown) => app.request('/api/auth/password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }) });
assert.deepEqual(await (await app.request('/api/auth/config')).json(), { mode: 'sso' });
assert.equal((await login(process.env.BI_AUTH_PASSWORD)).status, 403);
const ssoToken = jwt.sign({ userId: 'sso-user', roles: [] }, process.env.JWT_SECRET);
assert.equal(verifyAuthToken(ssoToken).userId, 'sso-user');
process.env.BI_AUTH_MODE = 'password';
assert.throws(() => verifyAuthToken(ssoToken));
assert.equal((await login('wrong')).status, 401);
process.env.DB_READ_ONLY = '1';
const response = await login(process.env.BI_AUTH_PASSWORD);
assert.equal(response.status, 200);
const { token, user } = await response.json();
assert.deepEqual(user.roles, ['BI-LEADER-ENERGY']);
const headers = { Authorization: `Bearer ${token}` };
assert.equal((await app.request('/api/data', { headers })).status, 200);
const payload = verifyAuthToken(token);
assert.equal(Number(payload.exp) - Number(payload.iat), 8 * 60 * 60);
process.env.DB_READ_ONLY = '0';
assert.equal((await app.request('/api/data', { method: 'POST', headers })).status, 403);
assert.equal((await app.request('/api/data')).status, 401);
assert.throws(() => verifyAuthToken(jwt.sign({ authMethod: 'password' }, 'wrong-key')));
process.env.BI_AUTH_PASSWORD = 'a-different-password-123456';
assert.equal((await app.request('/api/data', { headers })).status, 401);
process.env.BI_AUTH_MODE = 'sso';
assert.equal((await app.request('/api/data', { headers })).status, 401);
process.env.BI_AUTH_MODE = 'password';
delete process.env.BI_AUTH_PASSWORD;
assert.equal((await login('')).status, 503);
process.env.BI_AUTH_PASSWORD = 'a-different-password-123456';
for (let i = 0; i < 20; i++) await login('wrong');
assert.equal((await login(process.env.BI_AUTH_PASSWORD)).status, 429);
} finally {
for (const key of ['BI_AUTH_MODE', 'BI_AUTH_PASSWORD', 'JWT_SECRET', 'DEV_BYPASS_AUTH', 'DB_READ_ONLY']) {
if (previous[key] === undefined) delete process.env[key]; else process.env[key] = previous[key];
}
}
});