0
Fork 0
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:
Juan Picado @jotadeveloper 2018-06-30 01:20:27 +02:00
parent a7fd3605d1
commit 2e157fb134
No known key found for this signature in database
GPG key ID: 18AC54485952D158
8 changed files with 214 additions and 66 deletions

63
src/lib/config-utils.js Normal file
View 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;
}

View file

@ -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) {

View file

@ -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: '**',
};

View 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');
});
});
});

View 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

View file

@ -0,0 +1,9 @@
packages:
'@*/*':
access: $all
publish: $authenticated
proxy: npmjs
'**':
access: $all
publish: $authenticated
proxy: npmjs

View file

@ -0,0 +1,4 @@
packages:
'private':
access: admin
publish: admin

View file

@ -0,0 +1,9 @@
packages:
'@*/*':
access: $all
publish: admin superadmin
proxy: npmjs
'**':
access: $all user1 user2
publish: admin
proxy: npmjs