feat(auth): add configurable password login with SSO default
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-authored-by: HiFox Agent <agents-noreply@hifox.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
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];
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user