chore: sync with verdaccio master

This commit is contained in:
Juan Picado @jotadeveloper
2019-04-04 21:23:40 +02:00
parent 133a5f0171
commit 3506f32ad8
241 changed files with 57691 additions and 1932 deletions

158
src/lib/constants.js Normal file
View File

@@ -0,0 +1,158 @@
/**
* @prettier
*/
// @flow
export const DEFAULT_PORT: string = '4873';
export const DEFAULT_PROTOCOL: string = 'http';
export const DEFAULT_DOMAIN: string = 'localhost';
export const TIME_EXPIRATION_24H: string = '24h';
export const TIME_EXPIRATION_7D: string = '7d';
export const DIST_TAGS = 'dist-tags';
export const USERS = 'users';
export const DEFAULT_MIN_LIMIT_PASSWORD: number = 3;
export const DEFAULT_USER = 'Anonymous';
export const keyPem = 'verdaccio-key.pem';
export const certPem = 'verdaccio-cert.pem';
export const csrPem = 'verdaccio-csr.pem';
export const HEADERS = {
JSON: 'application/json',
CONTENT_TYPE: 'Content-type',
TEXT_PLAIN: 'text/plain',
FORWARDED_PROTO: 'X-Forwarded-Proto',
ETAG: 'ETag',
JSON_CHARSET: 'application/json; charset=utf-8',
OCTET_STREAM: 'application/octet-stream; charset=utf-8',
TEXT_CHARSET: 'text/plain; charset=utf-8',
WWW_AUTH: 'WWW-Authenticate',
GZIP: 'gzip',
};
export const CHARACTER_ENCODING = {
UTF8: 'utf8',
};
export const HEADER_TYPE = {
CONTENT_ENCODING: 'content-encoding',
CONTENT_TYPE: 'content-type',
CONTENT_LENGTH: 'content-length',
ACCEPT_ENCODING: 'accept-encoding',
};
export const ERROR_CODE = {
token_required: 'token is required',
};
export const TOKEN_BASIC = 'Basic';
export const TOKEN_BEARER = 'Bearer';
export const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
export const DEFAULT_UPLINK = 'npmjs';
export const ROLES = {
$ALL: '$all',
ALL: 'all',
$AUTH: '$authenticated',
$ANONYMOUS: '$anonymous',
DEPRECATED_ALL: '@all',
DEPRECATED_AUTH: '@authenticated',
DEPRECATED_ANONYMOUS: '@anonymous',
};
export const HTTP_STATUS = {
OK: 200,
CREATED: 201,
MULTIPLE_CHOICES: 300,
NOT_MODIFIED: 304,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
CONFLICT: 409,
UNSUPPORTED_MEDIA: 415,
BAD_DATA: 422,
INTERNAL_ERROR: 500,
SERVICE_UNAVAILABLE: 503,
LOOP_DETECTED: 508,
};
export const API_MESSAGE = {
PKG_CREATED: 'created new package',
PKG_CHANGED: 'package changed',
PKG_REMOVED: 'package removed',
PKG_PUBLISHED: 'package published',
TARBALL_UPLOADED: 'tarball uploaded successfully',
TARBALL_REMOVED: 'tarball removed',
TAG_UPDATED: 'tags updated',
TAG_REMOVED: 'tag removed',
TAG_ADDED: 'package tagged',
LOGGED_OUT: 'Logged out',
};
export const SUPPORT_ERRORS = {
PLUGIN_MISSING_INTERFACE: 'the plugin does not provide implementation of the requested feature',
TFA_DISABLED: 'the two-factor authentication is not yet supported',
};
export const API_ERROR = {
PASSWORD_SHORT: (passLength: number = DEFAULT_MIN_LIMIT_PASSWORD) =>
`The provided password is too short. Please pick a password longer than ${passLength} characters.`,
MUST_BE_LOGGED: 'You must be logged in to publish packages.',
PLUGIN_ERROR: 'bug in the auth plugin system',
CONFIG_BAD_FORMAT: 'config file must be an object',
BAD_USERNAME_PASSWORD: 'bad username/password, access denied',
NO_PACKAGE: 'no such package available',
PACKAGE_CANNOT_BE_ADDED: 'this package cannot be added',
BAD_DATA: 'bad data',
NOT_ALLOWED: 'not allowed to access package',
NOT_ALLOWED_PUBLISH: 'not allowed to publish package',
INTERNAL_SERVER_ERROR: 'internal server error',
UNKNOWN_ERROR: 'unknown error',
NOT_PACKAGE_UPLINK: 'package does not exist on uplink',
UPLINK_OFFLINE_PUBLISH: 'one of the uplinks is down, refuse to publish',
UPLINK_OFFLINE: 'uplink is offline',
CONTENT_MISMATCH: 'content length mismatch',
NOT_FILE_UPLINK: "file doesn't exist on uplink",
MAX_USERS_REACHED: 'maximum amount of users reached',
VERSION_NOT_EXIST: "this version doesn't exist",
FILE_NOT_FOUND: 'File not found',
BAD_STATUS_CODE: 'bad status code',
PACKAGE_EXIST: 'this package is already present',
BAD_AUTH_HEADER: 'bad authorization header',
WEB_DISABLED: 'Web interface is disabled in the config file',
DEPRECATED_BASIC_HEADER: 'basic authentication is deprecated, please use JWT instead',
BAD_FORMAT_USER_GROUP: 'user groups is different than an array',
RESOURCE_UNAVAILABLE: 'resource unavailable',
BAD_PACKAGE_DATA: 'bad incoming package data',
USERNAME_PASSWORD_REQUIRED: 'username and password is required',
USERNAME_ALREADY_REGISTERED: 'username is already registered',
};
export const APP_ERROR = {
CONFIG_NOT_VALID: 'CONFIG: it does not look like a valid config file',
PROFILE_ERROR: 'profile unexpected error',
PASSWORD_VALIDATION: 'not valid password',
};
export const DEFAULT_NO_README = 'ERROR: No README data found!';
export const MODULE_NOT_FOUND = 'MODULE_NOT_FOUND';
export const WEB_TITLE = 'Verdaccio';
export const PACKAGE_ACCESS = {
SCOPE: '@*/*',
ALL: '**',
};
export const UPDATE_BANNER = {
CHANGELOG_URL: 'https://github.com/verdaccio/verdaccio/releases/tag/',
};
export const STORAGE = {
PACKAGE_FILE_NAME: 'package.json',
FILE_EXIST_ERROR: 'EEXISTS',
NO_SUCH_FILE_ERROR: 'ENOENT',
DEFAULT_REVISION: '0-0000000000000000',
};

73
src/lib/crypto-utils.js Normal file
View File

@@ -0,0 +1,73 @@
/**
* @prettier
* @flow
*/
import { createDecipher, createCipher, createHash, pseudoRandomBytes } from 'crypto';
import jwt from 'jsonwebtoken';
import type { JWTSignOptions, RemoteUser } from '@verdaccio/types';
export const defaultAlgorithm = 'aes192';
export const defaultTarballHashAlgorithm = 'sha1';
export function aesEncrypt(buf: Buffer, secret: string): Buffer {
// deprecated
// https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options
const c = createCipher(defaultAlgorithm, secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
}
export function aesDecrypt(buf: Buffer, secret: string) {
try {
// deprecated
// https://nodejs.org/api/crypto.html#crypto_crypto_createdecipher_algorithm_password_options
const c = createDecipher(defaultAlgorithm, secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
} catch (_) {
return new Buffer(0);
}
}
export function createTarballHash() {
return createHash(defaultTarballHashAlgorithm);
}
/**
* Express doesn't do ETAGS with requests <= 1024b
* we use md5 here, it works well on 1k+ bytes, but sucks with fewer data
* could improve performance using crc32 after benchmarks.
* @param {Object} data
* @return {String}
*/
export function stringToMD5(data: Buffer | string) {
return createHash('md5')
.update(data)
.digest('hex');
}
export function generateRandomHexString(length: number = 8) {
return pseudoRandomBytes(length).toString('hex');
}
export async function signPayload(payload: RemoteUser, secretOrPrivateKey: string, options: JWTSignOptions): Promise<string> {
return new Promise(function(resolve, reject) {
return jwt.sign(
payload,
secretOrPrivateKey,
{
notBefore: '1', // Make sure the time will not rollback :)
...options,
},
(error, token) => (error ? reject(error) : resolve(token))
);
});
}
export function verifyPayload(token: string, secretOrPrivateKey: string) {
return jwt.verify(token, secretOrPrivateKey);
}

5
src/utils/string.js Normal file
View File

@@ -0,0 +1,5 @@
// @flow
export function spliceURL(...args: Array<string>): string {
return Array.from(args).reduce((lastResult, current) => lastResult + current).replace(/([^:])(\/)+(.)/g, `$1/$3`);
}

47
src/utils/user.js Normal file
View File

@@ -0,0 +1,47 @@
// @flow
import {stringToMD5} from '../lib/crypto-utils';
import _ from 'lodash';
// this is a generic avatar
// https://www.iconfinder.com/icons/403017/anonym_avatar_default_head_person_unknown_user_icon
// license: free commercial usage
export const GENERIC_AVATAR: string = `
data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1
ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYW
NrZ3JvdW5kPSJuZXcgLTI3IDI0IDEwMCAxMDAiIGhlaWdodD0iMTAwcHgiIGlkPSJ1bmtub3duIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3
g9Ii0yNyAyNCAxMDAgMTAwIiB3aWR0aD0iMTAwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy
8yMDAwL3N2ZyIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiIHhtbG5zOnhsaW5rPS
JodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48Zz48Zz48ZGVmcz48Y2lyY2xlIGN4PSIyMyIgY3k9Ijc0IiBpZD0iY2lyY2xlIi
ByPSI1MCIvPjwvZGVmcz48dXNlIGZpbGw9IiNGNUVFRTUiIG92ZXJmbG93PSJ2aXNpYmxlIiB4bGluazpocmVmPSIjY2lyY2xlIi8+PGN
saXBQYXRoIGlkPSJjaXJjbGVfMV8iPjx1c2Ugb3ZlcmZsb3c9InZpc2libGUiIHhsaW5rOmhyZWY9IiNjaXJjbGUiLz48L2NsaXBQYXR
oPjxnIGNsaXAtcGF0aD0idXJsKCNjaXJjbGVfMV8pIj48ZGVmcz48cGF0aCBkPSJNMzYsOTUuOWMwLDQsNC43LDUuMiw3LjEsNS44Yzc
uNiwyLDIyLjgsNS45LDIyLjgsNS45YzMuMiwxLjEsNS43LDMuNSw3LjEsNi42djkuOEgtMjd2LTkuOCAgICAgICBjMS4zLTMuMSwzLjk
tNS41LDcuMS02LjZjMCwwLDE1LjItMy45LDIyLjgtNS45YzIuNC0wLjYsNy4xLTEuOCw3LjEtNS44YzAtNCwwLTEwLjksMC0xMC45aDI
2QzM2LDg1LDM2LDkxLjksMzYsOTUuOXoiIGlkPSJzaG91bGRlcnMiLz48L2RlZnM+PHVzZSBmaWxsPSIjRTZDMTlDIiBvdmVyZmxvdz0
idmlzaWJsZSIgeGxpbms6aHJlZj0iI3Nob3VsZGVycyIvPjxjbGlwUGF0aCBpZD0ic2hvdWxkZXJzXzFfIj48dXNlIG92ZXJmbG93PSJ
2aXNpYmxlIiB4bGluazpocmVmPSIjc2hvdWxkZXJzIi8+PC9jbGlwUGF0aD48cGF0aCBjbGlwLXBhdGg9InVybCgjc2hvdWxkZXJzXzF
fKSIgZD0iTTIzLjIsMzVjMC4xLDAsMC4xLDAsMC4yLDBjMCwwLDAsMCwwLDAgICAgICBjMy4zLDAsOC4yLDAuMiwxMS40LDJjMy4zLD
EuOSw3LjMsNS42LDguNSwxMi4xYzIuNCwxMy43LTIuMSwzNS40LTYuMyw0Mi40Yy00LDYuNy05LjgsOS4yLTEzLjUsOS40YzAsMC0wL
jEsMC0wLjEsMCAgICAgIGMtMC4xLDAtMC4xLDAtMC4yLDBjLTAuMSwwLTAuMSwwLTAuMiwwYzAsMC0wLjEsMC0wLjEsMGMtMy43LTAuM
i05LjUtMi43LTEzLjUtOS40Yy00LjItNy04LjctMjguNy02LjMtNDIuNCAgICAgIGMxLjItNi41LDUuMi0xMC4yLDguNS0xMi4xYzMuM
i0xLjgsOC4xLTIsMTEuNC0yYzAsMCwwLDAsMCwwQzIzLjEsMzUsMjMuMSwzNSwyMy4yLDM1TDIzLjIsMzV6IiBmaWxsPSIjRDRCMDhDI
iBpZD0iaGVhZC1zaGFkb3ciLz48L2c+PC9nPjxwYXRoIGQ9Ik0yMi42LDQwYzE5LjEsMCwyMC43LDEzLjgsMjAuOCwxNS4xYzEuMSwxM
S45LTMsMjguMS02LjgsMzMuN2MtNCw1LjktOS44LDguMS0xMy41LDguMyAgICBjLTAuMiwwLTAuMiwwLTAuMywwYy0wLjEsMC0wLjEs
MC0wLjIsMEMxOC44LDk2LjgsMTMsOTQuNiw5LDg4LjdjLTMuOC01LjYtNy45LTIxLjgtNi44LTMzLjhDMi4zLDUzLjcsMy41LDQwLDIyL
jYsNDB6IiBmaWxsPSIjRjJDRUE1IiBpZD0iaGVhZCIvPjwvZz48L3N2Zz4=`;
/**
* Generate gravatar url from email address
*/
export function generateGravatarUrl(email: string = '', online: boolean = true): string {
if (online) {
if (_.isString(email) && _.size(email) > 0) {
email = email.trim().toLocaleLowerCase();
const emailMD5 = stringToMD5(email);
return `https://www.gravatar.com/avatar/${emailMD5}`;
}
return GENERIC_AVATAR;
} else {
return GENERIC_AVATAR;
}
}

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 380 B

View File

Before

Width:  |  Height:  |  Size: 898 B

After

Width:  |  Height:  |  Size: 898 B

View File

Before

Width:  |  Height:  |  Size: 833 B

After

Width:  |  Height:  |  Size: 833 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 265 B

After

Width:  |  Height:  |  Size: 265 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 464 B

After

Width:  |  Height:  |  Size: 464 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 977 B

After

Width:  |  Height:  |  Size: 977 B

View File

Before

Width:  |  Height:  |  Size: 851 B

After

Width:  |  Height:  |  Size: 851 B

View File

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 321 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 344 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 802 B

After

Width:  |  Height:  |  Size: 802 B

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Some files were not shown because too many files have changed in this diff Show More