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]; } } });