51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
import { PasswordService } from '../src/identity/password.service';
|
|
import { issueToken, hashToken } from '../src/identity/tokens';
|
|
import { createUserSchema, roleSchema } from '../src/identity/identity.schemas';
|
|
|
|
describe('passwords and token primitives', () => {
|
|
const passwords = new PasswordService();
|
|
it('salts equal passwords independently and verifies them', async () => {
|
|
const first = await passwords.hash('a secure long passphrase');
|
|
const second = await passwords.hash('a secure long passphrase');
|
|
expect(first).not.toBe(second);
|
|
expect(await passwords.verify('a secure long passphrase', first)).toBe(
|
|
true,
|
|
);
|
|
expect(await passwords.verify('wrong', first)).toBe(false);
|
|
await passwords.dummyVerify('anything');
|
|
});
|
|
it.each([
|
|
'',
|
|
'bcrypt$bad',
|
|
'scrypt-v1$x$x',
|
|
'scrypt-v1$' + 'a'.repeat(32) + '$' + 'a'.repeat(128) + '$extra',
|
|
])('fails closed for malformed stored hash %s', async (value) => {
|
|
expect(await passwords.verify('password', value)).toBe(false);
|
|
});
|
|
it('issues random URL-safe tokens and one-way digests', () => {
|
|
const first = issueToken();
|
|
expect(first.token).toMatch(/^[A-Za-z0-9_-]{43}$/);
|
|
expect(first.tokenHash).toMatch(/^[a-f0-9]{64}$/);
|
|
expect(hashToken(first.token)).toBe(first.tokenHash);
|
|
expect(issueToken().token).not.toBe(first.token);
|
|
});
|
|
it('validates long passphrases, known permissions and duplicate permissions', () => {
|
|
expect(
|
|
createUserSchema.safeParse({
|
|
email: 'a@example.com',
|
|
name: 'A',
|
|
password: 'short',
|
|
}).success,
|
|
).toBe(false);
|
|
expect(
|
|
roleSchema.safeParse({ name: 'Role', permissions: ['anything'] }).success,
|
|
).toBe(false);
|
|
expect(
|
|
roleSchema.safeParse({
|
|
name: 'Role',
|
|
permissions: ['users.read', 'users.read'],
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
});
|