618d4d674f
- Implement unit tests for password handling in `auth_test.ts`, covering bcrypt and legacy password hashes. - Create a fake database for user management to facilitate testing of the `AdminHandler`. - Validate JWT-based identity verification against database records. - Introduce tests for credential encryption and registry management in `security_test.ts`. - Ensure registry passwords are securely stored and can be decrypted correctly, including legacy support. - Add utility functions for password hashing and verification in `auth.ts`.
106 lines
2.9 KiB
TypeScript
106 lines
2.9 KiB
TypeScript
import { assert, assertEquals, fail } from '@std/assert';
|
|
|
|
import * as plugins from '../ts/plugins.ts';
|
|
import type { IUser as IDatabaseUser } from '../ts/types.ts';
|
|
import { AdminHandler } from '../ts/opsserver/handlers/admin.handler.ts';
|
|
import {
|
|
hashPassword,
|
|
isBcryptHash,
|
|
needsPasswordUpgrade,
|
|
verifyPassword,
|
|
} from '../ts/utils/auth.ts';
|
|
|
|
class FakeDatabase {
|
|
constructor(private users: Map<string, IDatabaseUser>) {}
|
|
|
|
getUserByUsername(username: string): IDatabaseUser | null {
|
|
return this.users.get(username) ?? null;
|
|
}
|
|
|
|
updateUserPassword(username: string, passwordHash: string): void {
|
|
const user = this.users.get(username);
|
|
if (!user) {
|
|
return;
|
|
}
|
|
|
|
this.users.set(username, {
|
|
...user,
|
|
passwordHash,
|
|
updatedAt: Date.now(),
|
|
});
|
|
}
|
|
}
|
|
|
|
async function createAdminHandler(users: IDatabaseUser[]): Promise<AdminHandler> {
|
|
const userMap = new Map(users.map((user) => [user.username, user]));
|
|
const fakeOpsServer = {
|
|
typedrouter: new plugins.typedrequest.TypedRouter(),
|
|
oneboxRef: {
|
|
database: new FakeDatabase(userMap),
|
|
},
|
|
};
|
|
|
|
const adminHandler = new AdminHandler(fakeOpsServer as any);
|
|
await adminHandler.initialize();
|
|
return adminHandler;
|
|
}
|
|
|
|
Deno.test('password helpers support bcrypt and legacy password hashes', async () => {
|
|
const password = 'correct horse battery staple';
|
|
const bcryptHash = await hashPassword(password);
|
|
|
|
assert(isBcryptHash(bcryptHash));
|
|
assert(await verifyPassword(password, bcryptHash));
|
|
assert(!(await verifyPassword('wrong password', bcryptHash)));
|
|
assert(!needsPasswordUpgrade(bcryptHash));
|
|
|
|
const legacyHash = btoa(password);
|
|
assert(await verifyPassword(password, legacyHash));
|
|
assert(needsPasswordUpgrade(legacyHash));
|
|
});
|
|
|
|
Deno.test('verified identity is derived from the signed JWT and database, not client fields', async () => {
|
|
const adminHandler = await createAdminHandler([
|
|
{
|
|
id: 1,
|
|
username: 'alice',
|
|
passwordHash: await hashPassword('password123'),
|
|
role: 'user',
|
|
createdAt: Date.now(),
|
|
updatedAt: Date.now(),
|
|
},
|
|
]);
|
|
|
|
const expiresAt = Date.now() + 60_000;
|
|
const jwt = await adminHandler.smartjwtInstance.createJWT({
|
|
userId: '1',
|
|
username: 'alice',
|
|
role: 'user',
|
|
status: 'loggedIn',
|
|
expiresAt,
|
|
});
|
|
|
|
const verifiedIdentity = await adminHandler.getVerifiedIdentity({
|
|
jwt,
|
|
userId: '999',
|
|
username: 'mallory',
|
|
role: 'admin',
|
|
expiresAt: 0,
|
|
});
|
|
|
|
assertEquals(verifiedIdentity.userId, '1');
|
|
assertEquals(verifiedIdentity.username, 'alice');
|
|
assertEquals(verifiedIdentity.role, 'user');
|
|
assertEquals(verifiedIdentity.expiresAt, expiresAt);
|
|
|
|
let rejected = false;
|
|
try {
|
|
await adminHandler.getVerifiedAdminIdentity(verifiedIdentity);
|
|
fail('Expected admin-only identity verification to reject non-admin users');
|
|
} catch {
|
|
rejected = true;
|
|
}
|
|
|
|
assert(rejected);
|
|
});
|