1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-11-13 03:35:52 +01:00
verdaccio/packages/core/htpasswd/tests/utils.test.ts
Juan Picado 3838d3d212 feat: relocate core packages (#1906)
* chore: clean lint warnings

* refactor: move core packages
2021-04-09 17:54:13 +02:00

253 lines
9.4 KiB
TypeScript

import crypto from 'crypto';
import { verifyPassword, lockAndRead, parseHTPasswd, addUserToHTPasswd, sanityCheck, changePasswordToHTPasswd, getCryptoPassword } from '../src/utils';
const mockReadFile = jest.fn();
const mockUnlockFile = jest.fn();
jest.mock('@verdaccio/file-locking', () => ({
readFile: () => mockReadFile(),
unlockFile: () => mockUnlockFile(),
}));
describe('parseHTPasswd', () => {
it('should parse the password for a single line', () => {
const input = 'test:$6b9MlB3WUELU:autocreated 2017-11-06T18:17:21.957Z';
const output = { test: '$6b9MlB3WUELU' };
expect(parseHTPasswd(input)).toEqual(output);
});
it('should parse the password for two lines', () => {
const input = `user1:$6b9MlB3WUELU:autocreated 2017-11-06T18:17:21.957Z
user2:$6FrCaT/v0dwE:autocreated 2017-12-14T13:30:20.838Z`;
const output = { user1: '$6b9MlB3WUELU', user2: '$6FrCaT/v0dwE' };
expect(parseHTPasswd(input)).toEqual(output);
});
it('should parse the password for multiple lines', () => {
const input = `user1:$6b9MlB3WUELU:autocreated 2017-11-06T18:17:21.957Z
user2:$6FrCaT/v0dwE:autocreated 2017-12-14T13:30:20.838Z
user3:$6FrCdfd\v0dwE:autocreated 2017-12-14T13:30:20.838Z
user4:$6FrCasdvppdwE:autocreated 2017-12-14T13:30:20.838Z`;
const output = {
user1: '$6b9MlB3WUELU',
user2: '$6FrCaT/v0dwE',
user3: '$6FrCdfd\v0dwE',
user4: '$6FrCasdvppdwE',
};
expect(parseHTPasswd(input)).toEqual(output);
});
});
describe('verifyPassword', () => {
it('should verify the MD5/Crypt3 password with true', () => {
const input = ['test', '$apr1$sKXK9.lG$rZ4Iy63Vtn8jF9/USc4BV0'];
expect(verifyPassword(input[0], input[1])).toBeTruthy();
});
it('should verify the MD5/Crypt3 password with false', () => {
const input = ['testpasswordchanged', '$apr1$sKXK9.lG$rZ4Iy63Vtn8jF9/USc4BV0'];
expect(verifyPassword(input[0], input[1])).toBeFalsy();
});
it('should verify the plain password with true', () => {
const input = ['testpasswordchanged', '{PLAIN}testpasswordchanged'];
expect(verifyPassword(input[0], input[1])).toBeTruthy();
});
it('should verify the plain password with false', () => {
const input = ['testpassword', '{PLAIN}testpasswordchanged'];
expect(verifyPassword(input[0], input[1])).toBeFalsy();
});
it('should verify the crypto SHA password with true', () => {
const input = ['testpassword', '{SHA}i7YRj4/Wk1rQh2o740pxfTJwj/0='];
expect(verifyPassword(input[0], input[1])).toBeTruthy();
});
it('should verify the crypto SHA password with false', () => {
const input = ['testpasswordchanged', '{SHA}i7YRj4/Wk1rQh2o740pxfTJwj/0='];
expect(verifyPassword(input[0], input[1])).toBeFalsy();
});
it('should verify the bcrypt password with true', () => {
const input = ['testpassword', '$2y$04$Wqed4yN0OktGbiUdxSTwtOva1xfESfkNIZfcS9/vmHLsn3.lkFxJO'];
expect(verifyPassword(input[0], input[1])).toBeTruthy();
});
it('should verify the bcrypt password with false', () => {
const input = ['testpasswordchanged', '$2y$04$Wqed4yN0OktGbiUdxSTwtOva1xfESfkNIZfcS9/vmHLsn3.lkFxJO'];
expect(verifyPassword(input[0], input[1])).toBeFalsy();
});
});
describe('addUserToHTPasswd - crypt3', () => {
beforeAll(() => {
// @ts-ignore
global.Date = jest.fn(() => {
return {
parse: jest.fn(),
toJSON: (): string => '2018-01-14T11:17:40.712Z',
};
});
crypto.randomBytes = jest.fn(() => {
return {
toString: (): string => '$6',
};
});
});
it('should add new htpasswd to the end', () => {
const input = ['', 'username', 'password'];
expect(addUserToHTPasswd(input[0], input[1], input[2])).toMatchSnapshot();
});
it('should add new htpasswd to the end in multiline input', () => {
const body = `test1:$6b9MlB3WUELU:autocreated 2017-11-06T18:17:21.957Z
test2:$6FrCaT/v0dwE:autocreated 2017-12-14T13:30:20.838Z`;
const input = [body, 'username', 'password'];
expect(addUserToHTPasswd(input[0], input[1], input[2])).toMatchSnapshot();
});
it('should throw an error for incorrect username with space', () => {
const [a, b, c] = ['', 'firstname lastname', 'password'];
expect(() => addUserToHTPasswd(a, b, c)).toThrowErrorMatchingSnapshot();
});
});
// ToDo: mock crypt3 with false
describe('addUserToHTPasswd - crypto', () => {
it('should create password with crypto', () => {
jest.resetModules();
jest.doMock('../src/crypt3.ts', () => false);
const input = ['', 'username', 'password'];
const utils = require('../src/utils.ts');
expect(utils.addUserToHTPasswd(input[0], input[1], input[2])).toEqual('username:{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=:autocreated 2018-01-14T11:17:40.712Z\n');
});
});
describe('lockAndRead', () => {
it('should call the readFile method', () => {
// console.log(fileLocking);
// const spy = jest.spyOn(fileLocking, 'readFile');
const cb = (): void => {};
lockAndRead('.htpasswd', cb);
expect(mockReadFile).toHaveBeenCalled();
});
});
describe('sanityCheck', () => {
let users;
beforeEach(() => {
users = { test: '$6FrCaT/v0dwE' };
});
test('should throw error for user already exists', () => {
const verifyFn = jest.fn();
const input = sanityCheck('test', users.test, verifyFn, users, Infinity);
expect(input.status).toEqual(401);
expect(input.message).toEqual('unauthorized access');
expect(verifyFn).toHaveBeenCalled();
});
test('should throw error for registration disabled of users', () => {
const verifyFn = (): void => {};
const input = sanityCheck('username', users.test, verifyFn, users, -1);
expect(input.status).toEqual(409);
expect(input.message).toEqual('user registration disabled');
});
test('should throw error max number of users', () => {
const verifyFn = (): void => {};
const input = sanityCheck('username', users.test, verifyFn, users, 1);
expect(input.status).toEqual(403);
expect(input.message).toEqual('maximum amount of users reached');
});
test('should not throw anything and sanity check', () => {
const verifyFn = (): void => {};
const input = sanityCheck('username', users.test, verifyFn, users, 2);
expect(input).toBeNull();
});
test('should throw error for required username field', () => {
const verifyFn = (): void => {};
const input = sanityCheck(undefined, users.test, verifyFn, users, 2);
expect(input.message).toEqual('username and password is required');
expect(input.status).toEqual(400);
});
test('should throw error for required password field', () => {
const verifyFn = (): void => {};
const input = sanityCheck('username', undefined, verifyFn, users, 2);
expect(input.message).toEqual('username and password is required');
expect(input.status).toEqual(400);
});
test('should throw error for required username & password fields', () => {
const verifyFn = (): void => {};
const input = sanityCheck(undefined, undefined, verifyFn, users, 2);
expect(input.message).toEqual('username and password is required');
expect(input.status).toEqual(400);
});
test('should throw error for existing username and password', () => {
const verifyFn = jest.fn(() => true);
const input = sanityCheck('test', users.test, verifyFn, users, 2);
expect(input.status).toEqual(409);
expect(input.message).toEqual('username is already registered');
expect(verifyFn).toHaveBeenCalledTimes(1);
});
test('should throw error for existing username and password with max number of users reached', () => {
const verifyFn = jest.fn(() => true);
const input = sanityCheck('test', users.test, verifyFn, users, 1);
expect(input.status).toEqual(409);
expect(input.message).toEqual('username is already registered');
expect(verifyFn).toHaveBeenCalledTimes(1);
});
});
describe('changePasswordToHTPasswd', () => {
test('should throw error for wrong password', () => {
const body = 'test:$6b9MlB3WUELU:autocreated 2017-11-06T18:17:21.957Z';
try {
changePasswordToHTPasswd(body, 'test', 'somerandompassword', 'newPassword');
} catch (error) {
expect(error.message).toEqual('Invalid old Password');
}
});
test('should change the password', () => {
const body = 'root:$6qLTHoPfGLy2:autocreated 2018-08-20T13:38:12.164Z';
expect(changePasswordToHTPasswd(body, 'root', 'demo123', 'newPassword')).toMatchSnapshot();
});
test('should generate a different result on salt change', () => {
crypto.randomBytes = jest.fn(() => {
return {
toString: (): string => 'AB',
};
});
const body = 'root:$6qLTHoPfGLy2:autocreated 2018-08-20T13:38:12.164Z';
expect(changePasswordToHTPasswd(body, 'root', 'demo123', 'demo123')).toEqual('root:ABfaAAjDKIgfw:autocreated 2018-08-20T13:38:12.164Z');
});
test('should change the password when crypt3 is not available', () => {
jest.resetModules();
jest.doMock('../src/crypt3.ts', () => false);
const utils = require('../src/utils.ts');
const body = 'username:{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=:autocreated 2018-01-14T11:17:40.712Z';
expect(utils.changePasswordToHTPasswd(body, 'username', 'password', 'newPassword')).toEqual(
'username:{SHA}KD1HqTOO0RALX+Klr/LR98eZv9A=:autocreated 2018-01-14T11:17:40.712Z'
);
});
});
describe('getCryptoPassword', () => {
test('should return the password hash', () => {
const passwordHash = `{SHA}y9vkk2zovmMYTZ8uE/wkkjQ3G5o=`;
expect(getCryptoPassword('demo123')).toBe(passwordHash);
});
});