mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-27 13:39:44 -08:00
c378f60a25
## Summary Provide details about your pull request and what it adds, fixes, or changes. Photos and videos are recommended. Continue breaking down `UserManagementHelper.ts` ... #### How to test the change: 1. ... ## Issues fixed Include links to Github issue or Community forum post or **Linear ticket**: > Important in order to close automatically and provide context to reviewers ... ## Review / Merge checklist - [ ] PR title and summary are descriptive. **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** ([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md)) - [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created. - [ ] Tests included. > A bug is not considered fixed, unless a test is added to prevent it from happening again. A feature is not complete without tests. > > *(internal)* You can use Slack commands to trigger [e2e tests](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#a39f9e5ba64a48b58a71d81c837e8227) or [deploy test instance](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#f6a177d32bde4b57ae2da0b8e454bfce) or [deploy early access version on Cloud](https://www.notion.so/n8n/Cloudbot-3dbe779836004972b7057bc989526998?pvs=4#fef2d36ab02247e1a0f65a74f6fb534e).
105 lines
3 KiB
TypeScript
105 lines
3 KiB
TypeScript
import { PasswordUtility } from '@/services/password.utility';
|
|
import Container from 'typedi';
|
|
|
|
function toComponents(hash: string) {
|
|
const BCRYPT_HASH_REGEX =
|
|
/^\$(?<version>.{2})\$(?<costFactor>\d{2})\$(?<salt>.{22})(?<hashedPassword>.{31})$/;
|
|
|
|
const match = hash.match(BCRYPT_HASH_REGEX);
|
|
|
|
if (!match?.groups) throw new Error('Invalid bcrypt hash format');
|
|
|
|
return match.groups;
|
|
}
|
|
|
|
describe('PasswordUtility', () => {
|
|
const passwordUtility = Container.get(PasswordUtility);
|
|
|
|
describe('hash()', () => {
|
|
test('should hash a plaintext password', async () => {
|
|
const plaintext = 'abcd1234X';
|
|
const hashed = await passwordUtility.hash(plaintext);
|
|
|
|
const { version, costFactor, salt, hashedPassword } = toComponents(hashed);
|
|
|
|
expect(version).toBe('2a');
|
|
expect(costFactor).toBe('10');
|
|
expect(salt).toHaveLength(22);
|
|
expect(hashedPassword).toHaveLength(31);
|
|
});
|
|
});
|
|
|
|
describe('compare()', () => {
|
|
test('should return true on match', async () => {
|
|
const plaintext = 'abcd1234X';
|
|
const hashed = await passwordUtility.hash(plaintext);
|
|
|
|
const isMatch = await passwordUtility.compare(plaintext, hashed);
|
|
|
|
expect(isMatch).toBe(true);
|
|
});
|
|
|
|
test('should return false on mismatch', async () => {
|
|
const secondPlaintext = 'abcd1234Y';
|
|
const hashed = await passwordUtility.hash('abcd1234X');
|
|
|
|
const isMatch = await passwordUtility.compare(secondPlaintext, hashed);
|
|
|
|
expect(isMatch).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('validate()', () => {
|
|
test('should throw on empty password', () => {
|
|
const check = () => passwordUtility.validate();
|
|
|
|
expect(check).toThrowError('Password is mandatory');
|
|
});
|
|
|
|
test('should return same password if valid', () => {
|
|
const validPassword = 'abcd1234X';
|
|
|
|
const validated = passwordUtility.validate(validPassword);
|
|
|
|
expect(validated).toBe(validPassword);
|
|
});
|
|
|
|
test('should require at least one uppercase letter', () => {
|
|
const invalidPassword = 'abcd1234';
|
|
|
|
const failingCheck = () => passwordUtility.validate(invalidPassword);
|
|
|
|
expect(failingCheck).toThrowError('Password must contain at least 1 uppercase letter.');
|
|
});
|
|
|
|
test('should require at least one number', () => {
|
|
const validPassword = 'abcd1234X';
|
|
const invalidPassword = 'abcdEFGH';
|
|
|
|
const validated = passwordUtility.validate(validPassword);
|
|
|
|
expect(validated).toBe(validPassword);
|
|
|
|
const check = () => passwordUtility.validate(invalidPassword);
|
|
|
|
expect(check).toThrowError('Password must contain at least 1 number.');
|
|
});
|
|
|
|
test('should require a minimum length of 8 characters', () => {
|
|
const invalidPassword = 'a'.repeat(7);
|
|
|
|
const check = () => passwordUtility.validate(invalidPassword);
|
|
|
|
expect(check).toThrowError('Password must be 8 to 64 characters long.');
|
|
});
|
|
|
|
test('should require a maximum length of 64 characters', () => {
|
|
const invalidPassword = 'a'.repeat(65);
|
|
|
|
const check = () => passwordUtility.validate(invalidPassword);
|
|
|
|
expect(check).toThrowError('Password must be 8 to 64 characters long.');
|
|
});
|
|
});
|
|
});
|