2020-08-13 23:27:00 +02:00
|
|
|
import path from 'path';
|
2018-08-21 08:05:34 +02:00
|
|
|
import _ from 'lodash';
|
2020-08-13 23:27:00 +02:00
|
|
|
import { CHARACTER_ENCODING, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
2020-03-03 23:59:19 +01:00
|
|
|
|
|
|
|
import { configExample } from '@verdaccio/mock';
|
2020-08-13 23:27:00 +02:00
|
|
|
import { Config as AppConfig } from '@verdaccio/config';
|
|
|
|
import { setup } from '@verdaccio/logger';
|
2020-03-03 23:59:19 +01:00
|
|
|
|
2018-08-21 08:05:34 +02:00
|
|
|
import {
|
|
|
|
buildUserBuffer,
|
|
|
|
getAuthenticatedMessage,
|
2020-03-08 09:19:12 +01:00
|
|
|
aesDecrypt,
|
|
|
|
verifyPayload,
|
|
|
|
buildToken,
|
|
|
|
convertPayloadToBase64,
|
2020-03-08 10:13:56 +01:00
|
|
|
parseConfigFile,
|
|
|
|
createAnonymousRemoteUser,
|
|
|
|
createRemoteUser,
|
2020-08-13 23:27:00 +02:00
|
|
|
signPayload,
|
2020-03-03 23:59:19 +01:00
|
|
|
} from '@verdaccio/utils';
|
2018-08-21 08:05:34 +02:00
|
|
|
|
2020-08-11 07:21:51 +02:00
|
|
|
import { IAuth } from '@verdaccio/dev-types';
|
2020-08-13 23:27:00 +02:00
|
|
|
import { Config, Security, RemoteUser } from '@verdaccio/types';
|
|
|
|
import { Auth } from '../src';
|
|
|
|
import { getMiddlewareCredentials, getApiToken, verifyJWTPayload, getSecurity } from '../src';
|
2020-03-08 09:19:12 +01:00
|
|
|
|
2019-02-24 23:20:25 +01:00
|
|
|
setup([]);
|
2018-08-21 08:05:34 +02:00
|
|
|
|
2020-03-03 23:59:19 +01:00
|
|
|
const parseConfigurationFile = (conf) => {
|
|
|
|
const { name, ext } = path.parse(conf);
|
|
|
|
const format = ext.startsWith('.') ? ext.substring(1) : 'yaml';
|
|
|
|
|
|
|
|
return path.join(__dirname, `./partials/config/${format}/security/${name}.${format}`);
|
|
|
|
};
|
|
|
|
|
2018-08-21 08:05:34 +02:00
|
|
|
describe('Auth utilities', () => {
|
2019-07-27 07:20:30 +02:00
|
|
|
jest.setTimeout(20000);
|
|
|
|
|
2018-08-21 08:05:34 +02:00
|
|
|
const parseConfigurationSecurityFile = (name) => {
|
|
|
|
return parseConfigurationFile(`security/${name}`);
|
|
|
|
};
|
|
|
|
|
|
|
|
function getConfig(configFileName: string, secret: string) {
|
|
|
|
const conf = parseConfigFile(parseConfigurationSecurityFile(configFileName));
|
2020-08-11 07:21:51 +02:00
|
|
|
// @ts-ignore
|
2020-08-13 23:27:00 +02:00
|
|
|
const secConf = _.merge(configExample(), conf);
|
2018-08-21 08:05:34 +02:00
|
|
|
secConf.secret = secret;
|
|
|
|
const config: Config = new AppConfig(secConf);
|
|
|
|
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function signCredentials(
|
|
|
|
configFileName: string,
|
|
|
|
username: string,
|
|
|
|
password: string,
|
|
|
|
secret = '12345',
|
|
|
|
methodToSpy: string,
|
2020-08-13 23:27:00 +02:00
|
|
|
methodNotBeenCalled: string
|
|
|
|
): Promise<string> {
|
2019-07-16 08:40:01 +02:00
|
|
|
const config: Config = getConfig(configFileName, secret);
|
|
|
|
const auth: IAuth = new Auth(config);
|
|
|
|
// @ts-ignore
|
|
|
|
const spy = jest.spyOn(auth, methodToSpy);
|
|
|
|
// @ts-ignore
|
|
|
|
const spyNotCalled = jest.spyOn(auth, methodNotBeenCalled);
|
|
|
|
const user: RemoteUser = {
|
|
|
|
name: username,
|
|
|
|
real_groups: [],
|
2020-08-13 23:27:00 +02:00
|
|
|
groups: [],
|
2019-07-16 08:40:01 +02:00
|
|
|
};
|
|
|
|
const token = await getApiToken(auth, config, user, password);
|
|
|
|
expect(spy).toHaveBeenCalled();
|
|
|
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
|
|
expect(spyNotCalled).not.toHaveBeenCalled();
|
|
|
|
expect(token).toBeDefined();
|
|
|
|
|
|
|
|
return token;
|
2018-08-21 08:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const verifyJWT = (token: string, user: string, password: string, secret: string) => {
|
|
|
|
const payload = verifyPayload(token, secret);
|
|
|
|
expect(payload.name).toBe(user);
|
|
|
|
expect(payload.groups).toBeDefined();
|
|
|
|
expect(payload.real_groups).toBeDefined();
|
|
|
|
};
|
|
|
|
|
|
|
|
const verifyAES = (token: string, user: string, password: string, secret: string) => {
|
2020-03-03 23:59:19 +01:00
|
|
|
const payload = aesDecrypt(convertPayloadToBase64(token), secret).toString(CHARACTER_ENCODING.UTF8);
|
2018-08-21 08:05:34 +02:00
|
|
|
const content = payload.split(':');
|
|
|
|
|
|
|
|
expect(content[0]).toBe(user);
|
|
|
|
expect(content[0]).toBe(password);
|
|
|
|
};
|
|
|
|
|
|
|
|
describe('getApiToken test', () => {
|
|
|
|
test('should sign token with aes and security missing', async () => {
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-missing', 'test', 'test', '1234567', 'aesEncrypt', 'jwtEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
|
|
|
|
verifyAES(token, 'test', 'test', '1234567');
|
|
|
|
expect(_.isString(token)).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
2018-09-21 17:34:12 +02:00
|
|
|
test('should sign token with aes and security empty', async () => {
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-empty', 'test', 'test', '123456', 'aesEncrypt', 'jwtEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
|
|
|
|
verifyAES(token, 'test', 'test', '123456');
|
|
|
|
expect(_.isString(token)).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
|
|
|
test('should sign token with aes', async () => {
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-basic', 'test', 'test', '123456', 'aesEncrypt', 'jwtEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
|
|
|
|
verifyAES(token, 'test', 'test', '123456');
|
|
|
|
expect(_.isString(token)).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
|
|
|
test('should sign token with legacy and jwt disabled', async () => {
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-no-legacy', 'test', 'test', 'x8T#ZCx=2t', 'aesEncrypt', 'jwtEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
|
|
|
|
expect(_.isString(token)).toBeTruthy();
|
|
|
|
verifyAES(token, 'test', 'test', 'x8T#ZCx=2t');
|
|
|
|
});
|
|
|
|
|
|
|
|
test('should sign token with legacy enabled and jwt enabled', async () => {
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-jwt-legacy-enabled', 'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
|
|
|
|
verifyJWT(token, 'test', 'test', 'secret');
|
|
|
|
expect(_.isString(token)).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
|
|
|
test('should sign token with jwt enabled', async () => {
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-jwt', 'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
|
2019-07-16 08:40:01 +02:00
|
|
|
expect(_.isString(token)).toBeTruthy();
|
|
|
|
verifyJWT(token, 'test', 'test', 'secret');
|
2018-08-21 08:05:34 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
test('should sign with jwt whether legacy is disabled', async () => {
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-legacy-disabled', 'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
|
|
|
|
expect(_.isString(token)).toBeTruthy();
|
|
|
|
verifyJWT(token, 'test', 'test', 'secret');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('getAuthenticatedMessage test', () => {
|
|
|
|
test('should sign token with jwt enabled', () => {
|
2020-08-13 23:27:00 +02:00
|
|
|
expect(getAuthenticatedMessage('test')).toBe("you are authenticated as 'test'");
|
2018-08-21 08:05:34 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('getMiddlewareCredentials test', () => {
|
|
|
|
describe('should get AES credentials', () => {
|
|
|
|
test.concurrent('should unpack aes token and credentials', async () => {
|
2019-07-16 08:40:01 +02:00
|
|
|
const secret = 'secret';
|
|
|
|
const user = 'test';
|
|
|
|
const pass = 'test';
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-legacy', user, pass, secret, 'aesEncrypt', 'jwtEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
const config: Config = getConfig('security-legacy', secret);
|
|
|
|
const security: Security = getSecurity(config);
|
|
|
|
const credentials = getMiddlewareCredentials(security, secret, `Bearer ${token}`);
|
|
|
|
expect(credentials).toBeDefined();
|
2019-07-16 08:40:01 +02:00
|
|
|
// @ts-ignore
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials.user).toEqual(user);
|
2019-07-16 08:40:01 +02:00
|
|
|
// @ts-ignore
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials.password).toEqual(pass);
|
|
|
|
});
|
|
|
|
|
|
|
|
test.concurrent('should unpack aes token and credentials', async () => {
|
2019-07-16 08:40:01 +02:00
|
|
|
const secret = 'secret';
|
|
|
|
const user = 'test';
|
|
|
|
const pass = 'test';
|
2018-08-21 08:05:34 +02:00
|
|
|
const token = buildUserBuffer(user, pass).toString('base64');
|
|
|
|
const config: Config = getConfig('security-legacy', secret);
|
|
|
|
const security: Security = getSecurity(config);
|
|
|
|
const credentials = getMiddlewareCredentials(security, secret, `Basic ${token}`);
|
|
|
|
expect(credentials).toBeDefined();
|
2019-07-16 08:40:01 +02:00
|
|
|
// @ts-ignore
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials.user).toEqual(user);
|
2019-07-16 08:40:01 +02:00
|
|
|
// @ts-ignore
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials.password).toEqual(pass);
|
|
|
|
});
|
|
|
|
|
|
|
|
test.concurrent('should return empty credential wrong secret key', async () => {
|
2019-07-16 08:40:01 +02:00
|
|
|
const secret = 'secret';
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-legacy', 'test', 'test', secret, 'aesEncrypt', 'jwtEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
const config: Config = getConfig('security-legacy', secret);
|
|
|
|
const security: Security = getSecurity(config);
|
2020-03-03 23:59:19 +01:00
|
|
|
const credentials = getMiddlewareCredentials(security, 'BAD_SECRET', buildToken(TOKEN_BEARER, token));
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials).not.toBeDefined();
|
|
|
|
});
|
|
|
|
|
|
|
|
test.concurrent('should return empty credential wrong scheme', async () => {
|
2019-07-16 08:40:01 +02:00
|
|
|
const secret = 'secret';
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-legacy', 'test', 'test', secret, 'aesEncrypt', 'jwtEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
const config: Config = getConfig('security-legacy', secret);
|
|
|
|
const security: Security = getSecurity(config);
|
2020-03-03 23:59:19 +01:00
|
|
|
const credentials = getMiddlewareCredentials(security, secret, buildToken('BAD_SCHEME', token));
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials).not.toBeDefined();
|
|
|
|
});
|
|
|
|
|
|
|
|
test.concurrent('should return empty credential corrupted payload', async () => {
|
2019-07-16 08:40:01 +02:00
|
|
|
const secret = 'secret';
|
2018-08-21 08:05:34 +02:00
|
|
|
const config: Config = getConfig('security-legacy', secret);
|
|
|
|
const auth: IAuth = new Auth(config);
|
2020-11-03 07:55:08 +01:00
|
|
|
const token = auth.aesEncrypt(Buffer.from(`corruptedBuffer`)).toString('base64');
|
2018-08-21 08:05:34 +02:00
|
|
|
const security: Security = getSecurity(config);
|
2020-03-03 23:59:19 +01:00
|
|
|
const credentials = getMiddlewareCredentials(security, secret, buildToken(TOKEN_BEARER, token));
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials).not.toBeDefined();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-03-08 10:13:56 +01:00
|
|
|
describe('verifyJWTPayload', () => {
|
|
|
|
test('should fail on verify the token and return anonymous users', () => {
|
|
|
|
expect(verifyJWTPayload('fakeToken', 'secret')).toEqual(createAnonymousRemoteUser());
|
|
|
|
});
|
|
|
|
|
|
|
|
test('should fail on verify the token and return anonymous users', async () => {
|
|
|
|
const remoteUser = createRemoteUser('foo', []);
|
|
|
|
const token = await signPayload(remoteUser, '12345');
|
|
|
|
const verifiedToken = verifyJWTPayload(token, '12345');
|
|
|
|
expect(verifiedToken.groups).toEqual(remoteUser.groups);
|
|
|
|
expect(verifiedToken.name).toEqual(remoteUser.name);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-08-21 08:05:34 +02:00
|
|
|
describe('should get JWT credentials', () => {
|
|
|
|
test('should return anonymous whether token is corrupted', () => {
|
|
|
|
const config: Config = getConfig('security-jwt', '12345');
|
|
|
|
const security: Security = getSecurity(config);
|
2020-03-03 23:59:19 +01:00
|
|
|
const credentials = getMiddlewareCredentials(security, '12345', buildToken(TOKEN_BEARER, 'fakeToken'));
|
2018-08-21 08:05:34 +02:00
|
|
|
|
|
|
|
expect(credentials).toBeDefined();
|
2019-07-16 08:40:01 +02:00
|
|
|
// @ts-ignore
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials.name).not.toBeDefined();
|
2019-07-16 08:40:01 +02:00
|
|
|
// @ts-ignore
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials.real_groups).toBeDefined();
|
2019-07-16 08:40:01 +02:00
|
|
|
// @ts-ignore
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials.real_groups).toEqual([]);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('should return anonymous whether token and scheme are corrupted', () => {
|
|
|
|
const config: Config = getConfig('security-jwt', '12345');
|
|
|
|
const security: Security = getSecurity(config);
|
2020-03-03 23:59:19 +01:00
|
|
|
const credentials = getMiddlewareCredentials(security, '12345', buildToken('FakeScheme', 'fakeToken'));
|
2018-08-21 08:05:34 +02:00
|
|
|
|
|
|
|
expect(credentials).not.toBeDefined();
|
|
|
|
});
|
|
|
|
|
|
|
|
test('should verify succesfully a JWT token', async () => {
|
2019-07-16 08:40:01 +02:00
|
|
|
const secret = 'secret';
|
|
|
|
const user = 'test';
|
2018-08-21 08:05:34 +02:00
|
|
|
const config: Config = getConfig('security-jwt', secret);
|
2020-08-13 23:27:00 +02:00
|
|
|
const token = await signCredentials('security-jwt', user, 'secretTest', secret, 'jwtEncrypt', 'aesEncrypt');
|
2018-08-21 08:05:34 +02:00
|
|
|
const security: Security = getSecurity(config);
|
2020-03-03 23:59:19 +01:00
|
|
|
const credentials = getMiddlewareCredentials(security, secret, buildToken(TOKEN_BEARER, token));
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials).toBeDefined();
|
2019-07-16 08:40:01 +02:00
|
|
|
// @ts-ignore
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials.name).toEqual(user);
|
2019-07-16 08:40:01 +02:00
|
|
|
// @ts-ignore
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials.real_groups).toBeDefined();
|
2019-07-16 08:40:01 +02:00
|
|
|
// @ts-ignore
|
2018-08-21 08:05:34 +02:00
|
|
|
expect(credentials.real_groups).toEqual([]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|