mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-01-27 22:59:51 -05:00
e939ca24af
Replaces default auth plugin verdaccio-htpasswd@10.x by verdaccio-htpasswd@11.x which is being used in verdaccio 6.x (almost identical) Apply backward compabiity Reduces maintenance (monorepo plugin can be removed) One more step to switch v6.x Add Node.js 12 GH Action for check backward compatibility
461 lines
14 KiB
TypeScript
461 lines
14 KiB
TypeScript
import _ from 'lodash';
|
|
import nock from 'nock';
|
|
|
|
import { Config, UpLinkConf } from '@verdaccio/types';
|
|
|
|
import AppConfig from '../../../../src/lib/config';
|
|
import {
|
|
API_ERROR,
|
|
ERROR_CODE,
|
|
HTTP_STATUS,
|
|
TOKEN_BASIC,
|
|
TOKEN_BEARER,
|
|
} from '../../../../src/lib/constants';
|
|
import { setup } from '../../../../src/lib/logger';
|
|
import ProxyStorage from '../../../../src/lib/up-storage';
|
|
import { DOMAIN_SERVERS } from '../../../functional/config.functional';
|
|
import { mockServer } from '../../__helper/mock';
|
|
import configExample from '../../partials/config';
|
|
|
|
setup({});
|
|
|
|
describe('UpStorage', () => {
|
|
const mockServerPort = 55547;
|
|
let mockRegistry;
|
|
const uplinkDefault = {
|
|
url: `http://localhost:${mockServerPort}`,
|
|
};
|
|
const generateProxy = (config: UpLinkConf = uplinkDefault) => {
|
|
const appConfig: Config = new AppConfig(configExample());
|
|
|
|
return new ProxyStorage(config, appConfig);
|
|
};
|
|
|
|
beforeAll(async () => {
|
|
mockRegistry = await mockServer(mockServerPort).init();
|
|
});
|
|
|
|
beforeEach(() => {
|
|
nock.cleanAll();
|
|
});
|
|
|
|
afterAll(function (done) {
|
|
mockRegistry[0].stop();
|
|
done();
|
|
});
|
|
|
|
test('should be defined', () => {
|
|
const proxy = generateProxy();
|
|
|
|
expect(proxy).toBeDefined();
|
|
});
|
|
|
|
describe('getRemoteMetadata', () => {
|
|
beforeEach(() => {
|
|
// @ts-ignore
|
|
process.env.TOKEN_TEST_ENV = 'foo';
|
|
// @ts-ignore
|
|
process.env.NPM_TOKEN = 'foo';
|
|
});
|
|
|
|
afterEach(() => {
|
|
delete process.env.TOKEN_TEST_ENV;
|
|
delete process.env.NPM_TOKEN;
|
|
});
|
|
|
|
test('should be get remote metadata', (done) => {
|
|
const proxy = generateProxy();
|
|
|
|
proxy.getRemoteMetadata('jquery', {}, (err, data, etag) => {
|
|
expect(err).toBeNull();
|
|
expect(_.isString(etag)).toBeTruthy();
|
|
expect(data.name).toBe('jquery');
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should handle 404 on be get remote metadata', (done) => {
|
|
nock('http://localhost:55547').get(`/jquery`).once().reply(404, { name: 'jquery' });
|
|
const proxy = generateProxy();
|
|
|
|
proxy.getRemoteMetadata('jquery', {}, (err) => {
|
|
expect(err).not.toBeNull();
|
|
expect(err.message).toMatch(/package does not exist on uplink/);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should handle 500 on be get remote metadata', (done) => {
|
|
nock('http://localhost:55547').get(`/jquery`).once().reply(500, { name: 'jquery' });
|
|
const proxy = generateProxy();
|
|
|
|
proxy.getRemoteMetadata('jquery', {}, (err) => {
|
|
expect(err).not.toBeNull();
|
|
expect(err.message).toMatch(/bad status code: 500/);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should be get remote metadata with etag', (done) => {
|
|
const proxy = generateProxy();
|
|
|
|
proxy.getRemoteMetadata('jquery', { etag: '123456' }, (err, data, etag) => {
|
|
expect(err).toBeNull();
|
|
expect(_.isString(etag)).toBeTruthy();
|
|
expect(data.name).toBe('jquery');
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should be get remote metadata package does not exist', (done) => {
|
|
const proxy = generateProxy();
|
|
|
|
proxy.getRemoteMetadata('@verdaccio/fake-package', { etag: '123456' }, (err) => {
|
|
expect(err).not.toBeNull();
|
|
expect(err.statusCode).toBe(HTTP_STATUS.NOT_FOUND);
|
|
expect(err.message).toMatch(API_ERROR.NOT_PACKAGE_UPLINK);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should be get remote metadata with json when uplink is npmmirror', (done) => {
|
|
nock('https://registry.npmmirror.com').get(`/jquery`).reply(200, { name: 'jquery' });
|
|
const proxy = generateProxy({ url: 'https://registry.npmmirror.com' });
|
|
|
|
proxy.getRemoteMetadata('jquery', { json: true }, (err, data) => {
|
|
expect(err).toBeNull();
|
|
expect(data.name).toBe('jquery');
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should be get remote metadata with auth header bearer', (done) => {
|
|
nock('https://registry.npmmirror.com', {
|
|
reqheaders: {
|
|
authorization: 'Bearer foo',
|
|
},
|
|
})
|
|
.get(`/jquery`)
|
|
.reply(200, { name: 'jquery' });
|
|
const proxy = generateProxy({
|
|
url: 'https://registry.npmmirror.com',
|
|
auth: {
|
|
type: TOKEN_BEARER,
|
|
token: 'foo',
|
|
},
|
|
});
|
|
|
|
proxy.getRemoteMetadata('jquery', {}, (err, data, etag) => {
|
|
expect(err).toBeNull();
|
|
// expect(_.isString(etag)).toBeTruthy();
|
|
expect(data.name).toBe('jquery');
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should be get remote metadata with auth node env TOKEN_TEST_ENV header bearer', (done) => {
|
|
nock('https://registry.npmmirror.com', {
|
|
reqheaders: {
|
|
authorization: 'Bearer foo',
|
|
},
|
|
})
|
|
.get(`/jquery`)
|
|
.reply(200, { name: 'jquery' });
|
|
const proxy = generateProxy({
|
|
url: 'https://registry.npmmirror.com',
|
|
auth: {
|
|
type: TOKEN_BEARER,
|
|
token_env: 'TOKEN_TEST_ENV',
|
|
},
|
|
});
|
|
|
|
proxy.getRemoteMetadata('jquery', {}, (err, data, etag) => {
|
|
expect(err).toBeNull();
|
|
expect(data.name).toBe('jquery');
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should be get remote metadata with auth node env NPM_TOKEN header bearer', (done) => {
|
|
nock('https://registry.npmmirror.com', {
|
|
reqheaders: {
|
|
authorization: 'Bearer foo',
|
|
},
|
|
})
|
|
.get(`/jquery`)
|
|
.reply(200, { name: 'jquery' });
|
|
const proxy = generateProxy({
|
|
url: 'https://registry.npmmirror.com',
|
|
auth: {
|
|
type: TOKEN_BEARER,
|
|
token_env: true,
|
|
},
|
|
});
|
|
|
|
proxy.getRemoteMetadata('jquery', {}, (err, data, etag) => {
|
|
expect(err).toBeNull();
|
|
expect(data.name).toBe('jquery');
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should be get remote metadata with auth header basic', (done) => {
|
|
nock('https://registry.npmmirror.com', {
|
|
reqheaders: {
|
|
authorization: 'Basic foo',
|
|
},
|
|
})
|
|
.get(`/jquery`)
|
|
.reply(200, { name: 'jquery' });
|
|
const proxy = generateProxy({
|
|
url: 'https://registry.npmmirror.com',
|
|
auth: {
|
|
type: TOKEN_BASIC,
|
|
token: 'foo',
|
|
},
|
|
});
|
|
|
|
proxy.getRemoteMetadata('jquery', {}, (err, data, etag) => {
|
|
expect(err).toBeNull();
|
|
// expect(_.isString(etag)).toBeTruthy();
|
|
expect(data.name).toBe('jquery');
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('error handling', () => {
|
|
test('should fails if auth type is missing', () => {
|
|
const proxy = generateProxy({
|
|
url: 'https://registry.npmmirror.com',
|
|
auth: {
|
|
type: TOKEN_BASIC,
|
|
token: undefined,
|
|
},
|
|
});
|
|
|
|
expect(function () {
|
|
proxy.getRemoteMetadata('jquery', {}, () => {});
|
|
}).toThrow(/token is required/);
|
|
});
|
|
|
|
test('should fails if token_env is undefined', () => {
|
|
const proxy = generateProxy({
|
|
url: 'https://registry.npmmirror.com',
|
|
auth: {
|
|
type: TOKEN_BASIC,
|
|
token_env: undefined,
|
|
},
|
|
});
|
|
|
|
expect(function () {
|
|
proxy.getRemoteMetadata('jquery', {}, () => {});
|
|
}).toThrow(ERROR_CODE.token_required);
|
|
});
|
|
|
|
test('should fails if token_env is false', () => {
|
|
const proxy = generateProxy({
|
|
url: 'https://registry.npmmirror.com',
|
|
auth: {
|
|
type: TOKEN_BASIC,
|
|
token_env: false,
|
|
},
|
|
});
|
|
|
|
expect(function () {
|
|
proxy.getRemoteMetadata('jquery', {}, () => {});
|
|
}).toThrow(ERROR_CODE.token_required);
|
|
});
|
|
|
|
test.skip('should fails if invalid token type', () => {
|
|
const proxy = generateProxy({
|
|
url: 'https://registry.npmmirror.com',
|
|
auth: {
|
|
token: 'SomethingWrong',
|
|
},
|
|
});
|
|
|
|
expect(function () {
|
|
proxy.getRemoteMetadata('jquery', {}, () => {});
|
|
}).toThrow(ERROR_CODE.token_required);
|
|
});
|
|
});
|
|
|
|
describe('fetchTarball', () => {
|
|
test.skip('should fetch a tarball from uplink', (done) => {
|
|
const proxy = generateProxy();
|
|
const tarball = `http://${DOMAIN_SERVERS}:${mockServerPort}/jquery/-/jquery-1.5.1.tgz`;
|
|
const stream = proxy.fetchTarball(tarball);
|
|
|
|
stream.on('error', function (err) {
|
|
expect(err).toBeNull();
|
|
done();
|
|
});
|
|
|
|
stream.on('content-length', function (contentLength) {
|
|
expect(contentLength).toBeDefined();
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should throw a 404 on fetch a tarball from uplink', (done) => {
|
|
const proxy = generateProxy();
|
|
const tarball = `http://${DOMAIN_SERVERS}:${mockServerPort}/jquery/-/no-exist-1.5.1.tgz`;
|
|
const stream = proxy.fetchTarball(tarball);
|
|
|
|
stream.on('error', function (err: any) {
|
|
expect(err).not.toBeNull();
|
|
expect(err.statusCode).toBe(HTTP_STATUS.NOT_FOUND);
|
|
expect(err.message).toMatch(API_ERROR.NOT_FILE_UPLINK);
|
|
|
|
done();
|
|
});
|
|
|
|
stream.on('content-length', function (contentLength) {
|
|
expect(contentLength).toBeDefined();
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('should be offline uplink', (done) => {
|
|
const proxy = generateProxy();
|
|
const tarball = 'http://404.verdaccioo.com';
|
|
const stream = proxy.fetchTarball(tarball);
|
|
expect(proxy.failed_requests).toBe(0);
|
|
|
|
// to test a uplink is offline we have to be try 3 times
|
|
// the default failed request are set to 2
|
|
process.nextTick(function () {
|
|
stream.on('error', function (err) {
|
|
expect(err).not.toBeNull();
|
|
// expect(err.statusCode).toBe(404);
|
|
expect(proxy.failed_requests).toBe(1);
|
|
|
|
const streamSecondTry = proxy.fetchTarball(tarball);
|
|
streamSecondTry.on('error', function (err) {
|
|
expect(err).not.toBeNull();
|
|
/*
|
|
code: 'ENOTFOUND',
|
|
errno: 'ENOTFOUND',
|
|
*/
|
|
// expect(err.statusCode).toBe(404);
|
|
expect(proxy.failed_requests).toBe(2);
|
|
const streamThirdTry = proxy.fetchTarball(tarball);
|
|
streamThirdTry.on('error', function (err: any) {
|
|
expect(err).not.toBeNull();
|
|
expect(err.statusCode).toBe(HTTP_STATUS.INTERNAL_ERROR);
|
|
expect(proxy.failed_requests).toBe(2);
|
|
expect(err.message).toMatch(API_ERROR.UPLINK_OFFLINE);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}, 10000);
|
|
});
|
|
|
|
describe('isUplinkValid', () => {
|
|
describe('valid use cases', () => {
|
|
const validateUpLink = (
|
|
url: string,
|
|
tarBallUrl = `${url}/artifactory/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz`
|
|
) => {
|
|
const uplinkConf = { url };
|
|
const proxy: any = generateProxy(uplinkConf);
|
|
|
|
return proxy.isUplinkValid(tarBallUrl);
|
|
};
|
|
|
|
test('should validate tarball path against uplink', () => {
|
|
expect(validateUpLink('https://artifactory.mydomain.com')).toBe(true);
|
|
});
|
|
|
|
test('should validate tarball path against uplink case#2', () => {
|
|
expect(validateUpLink('https://artifactory.mydomain.com:443')).toBe(true);
|
|
});
|
|
|
|
test('should validate tarball path against uplink case#3', () => {
|
|
expect(validateUpLink('http://localhost')).toBe(true);
|
|
});
|
|
|
|
test('should validate tarball path against uplink case#4', () => {
|
|
expect(validateUpLink('http://my.domain.test')).toBe(true);
|
|
});
|
|
|
|
test('should validate tarball path against uplink case#5', () => {
|
|
expect(validateUpLink('http://my.domain.test:3000')).toBe(true);
|
|
});
|
|
|
|
// corner case https://github.com/verdaccio/verdaccio/issues/571
|
|
test('should validate tarball path against uplink case#6', () => {
|
|
// same protocol, same domain, port === 443 which is also the standard for https
|
|
expect(
|
|
validateUpLink(
|
|
'https://my.domain.test',
|
|
`https://my.domain.test:443/artifactory/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz`
|
|
)
|
|
).toBe(true);
|
|
});
|
|
|
|
test('should validate tarball path against uplink case#7', () => {
|
|
expect(validateUpLink('https://artifactory.mydomain.com:5569')).toBe(true);
|
|
});
|
|
|
|
test('should validate tarball path against uplink case#8', () => {
|
|
expect(validateUpLink('https://localhost:5539')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('invalid use cases', () => {
|
|
test('should fails on validate tarball path against uplink', () => {
|
|
const url = 'https://artifactory.mydomain.com';
|
|
const tarBallUrl = 'https://localhost/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz';
|
|
const uplinkConf = { url };
|
|
const proxy: any = generateProxy(uplinkConf);
|
|
|
|
expect(proxy.isUplinkValid(tarBallUrl)).toBe(false);
|
|
});
|
|
|
|
test('should fails on validate tarball path against uplink case#2', () => {
|
|
// different domain same, same port, same protocol
|
|
const url = 'https://domain';
|
|
const tarBallUrl = 'https://localhost/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz';
|
|
const uplinkConf = { url };
|
|
const proxy: any = generateProxy(uplinkConf);
|
|
|
|
expect(proxy.isUplinkValid(tarBallUrl)).toBe(false);
|
|
});
|
|
|
|
test('should fails on validate tarball path against uplink case#3', () => {
|
|
// same domain, different protocol, different port
|
|
const url = 'http://localhost:5001';
|
|
const tarBallUrl = 'https://localhost:4000/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz';
|
|
const uplinkConf = { url };
|
|
const proxy: any = generateProxy(uplinkConf);
|
|
|
|
expect(proxy.isUplinkValid(tarBallUrl)).toBe(false);
|
|
});
|
|
|
|
test('should fails on validate tarball path against uplink case#4', () => {
|
|
// same domain, same protocol, different port
|
|
const url = 'https://subdomain.domain:5001';
|
|
const tarBallUrl =
|
|
'https://subdomain.domain:4000/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz';
|
|
const uplinkConf = { url };
|
|
const proxy: any = generateProxy(uplinkConf);
|
|
|
|
expect(proxy.isUplinkValid(tarBallUrl)).toBe(false);
|
|
});
|
|
|
|
test('should fails on validate tarball path against uplink case#5', () => {
|
|
// different protocol, different domain, different port
|
|
const url = 'https://subdomain.my:5001';
|
|
const tarBallUrl = 'http://subdomain.domain:4000/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz';
|
|
const uplinkConf = { url };
|
|
const proxy: any = generateProxy(uplinkConf);
|
|
|
|
expect(proxy.isUplinkValid(tarBallUrl)).toBe(false);
|
|
});
|
|
});
|
|
});
|
|
});
|