mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-16 21:56:25 -05:00
refactor: config unit test
match package access normalise
This commit is contained in:
parent
a7fd3605d1
commit
2e157fb134
8 changed files with 214 additions and 66 deletions
63
src/lib/config-utils.js
Normal file
63
src/lib/config-utils.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
import minimatch from 'minimatch';
|
||||
import assert from 'assert';
|
||||
|
||||
/**
|
||||
* Normalise user list.
|
||||
* @return {Array}
|
||||
*/
|
||||
export function normalizeUserlist(oldFormat: any, newFormat: any) {
|
||||
let result = [];
|
||||
/* eslint prefer-rest-params: "off" */
|
||||
|
||||
for (let i=0; i < arguments.length; i++) {
|
||||
if (arguments[i] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it's a string, split it to array
|
||||
if (typeof(arguments[i]) === 'string') {
|
||||
result.push(arguments[i].split(/\s+/));
|
||||
} else if (Array.isArray(arguments[i])) {
|
||||
result.push(arguments[i]);
|
||||
} else {
|
||||
throw Error('CONFIG: bad package acl (array or string expected): ' + JSON.stringify(arguments[i]));
|
||||
}
|
||||
}
|
||||
return _.flatten(result);
|
||||
}
|
||||
|
||||
export function getMatchedPackagesSpec(packages: any, pkg: any) {
|
||||
for (let i in packages) {
|
||||
// $FlowFixMe
|
||||
if (minimatch.makeRe(i).exec(pkg)) {
|
||||
return packages[i];
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
export function normalisePackageAccess(packages: any): any {
|
||||
const normalizedPkgs: any = {...packages};
|
||||
// add a default rule for all packages to make writing plugins easier
|
||||
if (_.isNil(normalizedPkgs['**'])) {
|
||||
normalizedPkgs['**'] = {};
|
||||
}
|
||||
|
||||
for (let pkg in packages) {
|
||||
if (Object.prototype.hasOwnProperty.call(packages, pkg)) {
|
||||
assert(_.isObject(packages[pkg]) && _.isArray(packages[pkg]) === false,
|
||||
`CONFIG: bad "'${pkg}'" package description (object expected)`);
|
||||
normalizedPkgs[pkg].access = normalizeUserlist(packages[pkg].allow_access, packages[pkg].access);
|
||||
delete normalizedPkgs[pkg].allow_access;
|
||||
normalizedPkgs[pkg].publish = normalizeUserlist(packages[pkg].allow_publish, packages[pkg].publish);
|
||||
delete normalizedPkgs[pkg].allow_publish;
|
||||
normalizedPkgs[pkg].proxy = normalizeUserlist(packages[pkg].proxy_access, packages[pkg].proxy);
|
||||
delete normalizedPkgs[pkg].proxy_access;
|
||||
}
|
||||
}
|
||||
|
||||
return normalizedPkgs;
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
import {generateRandomHexString} from './crypto-utils';
|
||||
import {normalisePackageAccess} from './config-utils';
|
||||
|
||||
const assert = require('assert');
|
||||
const _ = require('lodash');
|
||||
const Error = require('http-errors');
|
||||
// const Error = require('http-errors');
|
||||
const minimatch = require('minimatch');
|
||||
|
||||
const Utils = require('./utils');
|
||||
|
@ -12,24 +13,6 @@ const pkgName = module.exports.name;
|
|||
const strategicConfigProps = ['users', 'uplinks', 'packages'];
|
||||
const allowedEnvConfig = ['http_proxy', 'https_proxy', 'no_proxy'];
|
||||
|
||||
/**
|
||||
* [[a, [b, c]], d] -> [a, b, c, d]
|
||||
* @param {*} array
|
||||
* @return {Array}
|
||||
*/
|
||||
function flatten(array) {
|
||||
let result = [];
|
||||
for (let i=0; i < array.length; i++) {
|
||||
if (Array.isArray(array[i])) {
|
||||
/* eslint prefer-spread: "off" */
|
||||
result.push.apply(result, flatten(array[i]));
|
||||
} else {
|
||||
result.push(array[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function checkUserOrUplink(item, users) {
|
||||
assert(item !== 'all' && item !== 'owner'
|
||||
&& item !== 'anonymous' && item !== 'undefined' && item !== 'none', 'CONFIG: reserved user/uplink name: ' + item);
|
||||
|
@ -38,31 +21,6 @@ function checkUserOrUplink(item, users) {
|
|||
users[item] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalise user list.
|
||||
* @return {Array}
|
||||
*/
|
||||
function normalizeUserlist() {
|
||||
let result = [];
|
||||
/* eslint prefer-rest-params: "off" */
|
||||
|
||||
for (let i=0; i < arguments.length; i++) {
|
||||
if (arguments[i] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it's a string, split it to array
|
||||
if (typeof(arguments[i]) === 'string') {
|
||||
result.push(arguments[i].split(/\s+/));
|
||||
} else if (Array.isArray(arguments[i])) {
|
||||
result.push(arguments[i]);
|
||||
} else {
|
||||
throw Error('CONFIG: bad package acl (array or string expected): ' + JSON.stringify(arguments[i]));
|
||||
}
|
||||
}
|
||||
return flatten(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Coordinates the application configuration
|
||||
*/
|
||||
|
@ -133,28 +91,7 @@ class Config {
|
|||
}
|
||||
}
|
||||
|
||||
// add a default rule for all packages to make writing plugins easier
|
||||
if (self.packages['**'] == null) {
|
||||
self.packages['**'] = {};
|
||||
}
|
||||
|
||||
for (let pkg in self.packages) {
|
||||
if (Object.prototype.hasOwnProperty.call(self.packages, pkg)) {
|
||||
assert(
|
||||
typeof(self.packages[pkg]) === 'object' &&
|
||||
!Array.isArray(self.packages[pkg])
|
||||
, 'CONFIG: bad "'+pkg+'" package description (object expected)');
|
||||
|
||||
self.packages[pkg].access = normalizeUserlist(self.packages[pkg].allow_access, self.packages[pkg].access);
|
||||
delete self.packages[pkg].allow_access;
|
||||
|
||||
self.packages[pkg].publish = normalizeUserlist(self.packages[pkg].allow_publish, self.packages[pkg].publish);
|
||||
delete self.packages[pkg].allow_publish;
|
||||
|
||||
self.packages[pkg].proxy = normalizeUserlist(self.packages[pkg].proxy_access, self.packages[pkg].proxy);
|
||||
delete self.packages[pkg].proxy_access;
|
||||
}
|
||||
}
|
||||
self.packages = normalisePackageAccess(self.packages);
|
||||
|
||||
// loading these from ENV if aren't in config
|
||||
allowedEnvConfig.forEach((function(v) {
|
||||
|
|
|
@ -83,3 +83,8 @@ export const DEFAULT_NO_README = 'ERROR: No README data found!';
|
|||
|
||||
|
||||
export const WEB_TITLE = 'Verdaccio';
|
||||
|
||||
export const PACKAGE_ACCESS = {
|
||||
SCOPE: '@*/*',
|
||||
ALL: '**',
|
||||
};
|
||||
|
|
107
test/unit/api/config-utils.spec.js
Normal file
107
test/unit/api/config-utils.spec.js
Normal file
|
@ -0,0 +1,107 @@
|
|||
// @flow
|
||||
import path from 'path';
|
||||
import {spliceURL} from '../../../src/utils/string';
|
||||
import {parseConfigFile} from '../../../src/lib/utils';
|
||||
import {normalisePackageAccess} from '../../../src/lib/config-utils';
|
||||
import {PACKAGE_ACCESS, ROLES} from '../../../src/lib/constants';
|
||||
|
||||
describe('Config Utilities', () => {
|
||||
|
||||
const parsePartial = (name) => {
|
||||
return path.join(__dirname, `../partials/config/yaml/${name}.yaml`);
|
||||
};
|
||||
|
||||
describe('getMatchedPackagesSpec', () => {
|
||||
test('should test basic conversion', ()=> {
|
||||
const {packages} = parseConfigFile(parsePartial('pkgs-basic'));
|
||||
const access = normalisePackageAccess(packages);
|
||||
|
||||
expect(access).toBeDefined();
|
||||
const scoped = access[`${PACKAGE_ACCESS.SCOPE}`];
|
||||
const all = access[`${PACKAGE_ACCESS.ALL}`];
|
||||
|
||||
expect(scoped).toBeDefined();
|
||||
expect(all).toBeDefined();
|
||||
});
|
||||
|
||||
test('should test multi group', ()=> {
|
||||
const {packages} = parseConfigFile(parsePartial('pkgs-multi-group'));
|
||||
const access = normalisePackageAccess(packages);
|
||||
|
||||
expect(access).toBeDefined();
|
||||
const scoped = access[`${PACKAGE_ACCESS.SCOPE}`];
|
||||
|
||||
const all = access[`${PACKAGE_ACCESS.ALL}`];
|
||||
|
||||
expect(scoped).toBeDefined();
|
||||
expect(scoped.access).toContain('$all');
|
||||
expect(scoped.publish).toHaveLength(2);
|
||||
expect(scoped.publish).toContain('admin');
|
||||
expect(scoped.publish).toContain('superadmin');
|
||||
|
||||
expect(all).toBeDefined();
|
||||
expect(all.access).toHaveLength(3);
|
||||
expect(all.access).toContain('$all');
|
||||
expect(all.publish).toHaveLength(1);
|
||||
expect(all.publish).toContain('admin');
|
||||
|
||||
});
|
||||
|
||||
|
||||
test('should deprecated packages props', ()=> {
|
||||
const {packages} = parseConfigFile(parsePartial('deprecated-pkgs-basic'));
|
||||
const access = normalisePackageAccess(packages);
|
||||
|
||||
expect(access).toBeDefined();
|
||||
const scoped = access[`${PACKAGE_ACCESS.SCOPE}`];
|
||||
const all = access[`${PACKAGE_ACCESS.ALL}`];
|
||||
const react = access['react-*'];
|
||||
|
||||
expect(react).toBeDefined();
|
||||
expect(react.access[0]).toBe(ROLES.$ALL);
|
||||
expect(react.publish[0]).toBe('admin');
|
||||
expect(react.proxy[0]).toBe('uplink2');
|
||||
expect(react.storage).toBeDefined();
|
||||
|
||||
expect(react.storage).toBe('react-storage');
|
||||
expect(scoped).toBeDefined();
|
||||
expect(scoped.storage).not.toBeDefined();
|
||||
expect(all).toBeDefined();
|
||||
expect(all.access).toBeDefined();
|
||||
expect(all.storage).not.toBeDefined();
|
||||
expect(all.publish).toBeDefined();
|
||||
expect(all.proxy).toBeDefined();
|
||||
expect(all.allow_access).toBeUndefined();
|
||||
expect(all.allow_publish).toBeUndefined();
|
||||
expect(all.proxy_access).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should check not default packages access', ()=> {
|
||||
const {packages} = parseConfigFile(parsePartial('pkgs-empty'));
|
||||
const access = normalisePackageAccess(packages);
|
||||
expect(access).toBeDefined();
|
||||
const scoped = access[`${PACKAGE_ACCESS.SCOPE}`];
|
||||
expect(scoped).toBeUndefined();
|
||||
const all = access[`${PACKAGE_ACCESS.ALL}`];
|
||||
|
||||
expect(all).toBeDefined();
|
||||
expect(all.access).toBeUndefined();
|
||||
expect(all.publish).toBeUndefined();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('spliceURL', () => {
|
||||
test('should splice two strings and generate a url', () => {
|
||||
const url: string = spliceURL('http://domain.com', '/-/static/logo.png');
|
||||
|
||||
expect(url).toMatch('http://domain.com/-/static/logo.png');
|
||||
});
|
||||
|
||||
test('should splice a empty strings and generate a url', () => {
|
||||
const url: string = spliceURL('', '/-/static/logo.png');
|
||||
|
||||
expect(url).toMatch('/-/static/logo.png');
|
||||
});
|
||||
});
|
||||
});
|
14
test/unit/partials/config/yaml/deprecated-pkgs-basic.yaml
Normal file
14
test/unit/partials/config/yaml/deprecated-pkgs-basic.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
packages:
|
||||
'@*/*':
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
proxy: npmjs
|
||||
'react-*':
|
||||
allow_access: $all
|
||||
publish: admin
|
||||
proxy_access: uplink2
|
||||
storage: 'react-storage'
|
||||
'**':
|
||||
allow_access: $all
|
||||
allow_publish: $authenticated
|
||||
proxy_access: npmjs
|
9
test/unit/partials/config/yaml/pkgs-basic.yaml
Normal file
9
test/unit/partials/config/yaml/pkgs-basic.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
packages:
|
||||
'@*/*':
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
proxy: npmjs
|
||||
'**':
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
proxy: npmjs
|
4
test/unit/partials/config/yaml/pkgs-empty.yaml
Normal file
4
test/unit/partials/config/yaml/pkgs-empty.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
packages:
|
||||
'private':
|
||||
access: admin
|
||||
publish: admin
|
9
test/unit/partials/config/yaml/pkgs-multi-group.yaml
Normal file
9
test/unit/partials/config/yaml/pkgs-multi-group.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
packages:
|
||||
'@*/*':
|
||||
access: $all
|
||||
publish: admin superadmin
|
||||
proxy: npmjs
|
||||
'**':
|
||||
access: $all user1 user2
|
||||
publish: admin
|
||||
proxy: npmjs
|
Loading…
Reference in a new issue