mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-16 21:56:25 -05:00
refactor: max-len and printWidth to 100 (#1941)
* refactor: max-len and printWidth to 100 * chore: ci specific pnpm version * fix: add types peer package literally get rid of types package
This commit is contained in:
parent
1e48f1c077
commit
8c730c0694
172 changed files with 3044 additions and 949 deletions
|
@ -21,6 +21,7 @@
|
|||
|
||||
# do not copy over node_modules we will run `npm install` anyway
|
||||
node_modules
|
||||
website
|
||||
|
||||
# output from test runs and similar things
|
||||
*.log
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
"camelcase": "off",
|
||||
"guard-for-in": "error",
|
||||
"new-cap": "error",
|
||||
"max-len": ["warn", 160],
|
||||
"max-len": ["warn", 100],
|
||||
"no-console": ["error", { "allow": ["warn"] }],
|
||||
"no-constant-condition": "error",
|
||||
"no-debugger": "error",
|
||||
|
|
2
.github/workflows/changesets.yml
vendored
2
.github/workflows/changesets.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
|||
node-version: 14
|
||||
|
||||
- name: install pnpm
|
||||
run: npm install pnpm@5.5.2 -g
|
||||
run: npm i pnpm@5.5.2 -g
|
||||
|
||||
- name: setup pnpm config
|
||||
run: pnpm config set store-dir $PNPM_CACHE_FOLDER
|
||||
|
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -32,7 +32,7 @@ jobs:
|
|||
with:
|
||||
node_version: ${{ matrix.node_version }}
|
||||
- name: Install pnpm
|
||||
run: npm i -g pnpm
|
||||
run: npm i pnpm@5.5.12 -g
|
||||
- name: Install
|
||||
run: pnpm recursive install
|
||||
- name: Format
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"endOfLine": "lf",
|
||||
"useTabs": false,
|
||||
"printWidth": 160,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": true,
|
||||
|
|
|
@ -14,10 +14,10 @@ COPY . .
|
|||
|
||||
RUN npm -g i pnpm@latest && \
|
||||
pnpm config set registry $VERDACCIO_BUILD_REGISTRY && \
|
||||
pnpm recursive install --frozen-lockfile && \
|
||||
pnpm recursive install --frozen-lockfile --ignore-scripts && \
|
||||
pnpm run build && \
|
||||
pnpm run lint && \
|
||||
pnpm install --prod
|
||||
pnpm install --prod --ignore-scripts
|
||||
|
||||
FROM node:12.18.3-alpine
|
||||
LABEL maintainer="https://github.com/verdaccio/verdaccio"
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
"selfsigned": "1.10.7",
|
||||
"standard-version": "8.0.0",
|
||||
"supertest": "4.0.2",
|
||||
"typescript": ">=3.3.1 <3.10.0",
|
||||
"typescript": "^3.9.7",
|
||||
"verdaccio": "latest",
|
||||
"verdaccio-audit": "latest",
|
||||
"verdaccio-auth-memory": "latest",
|
||||
|
@ -101,8 +101,6 @@
|
|||
"verdaccio-memory": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node packages/verdaccio/debug/bootstrap.js",
|
||||
"debug": "node --inspect packages/verdaccio/debug/bootstrap.js",
|
||||
"dev": "cross-env BABEL_ENV=registry babel-node --extensions \".ts,.tsx\" packages/cli/src",
|
||||
"clean": "pnpm recursive run clean",
|
||||
"build": "pnpm recursive run build",
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/config": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/dev-types": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/server": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/types": "workspace:*",
|
||||
"body-parser": "1.19.0",
|
||||
|
|
|
@ -6,13 +6,17 @@ import { media, allow } from '@verdaccio/middleware';
|
|||
import { API_MESSAGE, HTTP_STATUS, DIST_TAGS } from '@verdaccio/dev-commons';
|
||||
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
import { Package } from '@verdaccio/types';
|
||||
|
||||
// @ts-ignore
|
||||
import { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler } from '@verdaccio/dev-types';
|
||||
import { IStorageHandler } from '@verdaccio/store';
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';
|
||||
|
||||
export default function (route: Router, auth: IAuth, storage: IStorageHandler): void {
|
||||
const can = allow(auth);
|
||||
const tag_package_version = function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): $NextFunctionVer {
|
||||
const tag_package_version = function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): $NextFunctionVer {
|
||||
if (_.isString(req.body) === false) {
|
||||
return next('route');
|
||||
}
|
||||
|
@ -31,11 +35,25 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler):
|
|||
// tagging a package.
|
||||
route.put('/:package/:tag', can('publish'), media(mime.getType('json')), tag_package_version);
|
||||
|
||||
route.post('/-/package/:package/dist-tags/:tag', can('publish'), media(mime.getType('json')), tag_package_version);
|
||||
route.post(
|
||||
'/-/package/:package/dist-tags/:tag',
|
||||
can('publish'),
|
||||
media(mime.getType('json')),
|
||||
tag_package_version
|
||||
);
|
||||
|
||||
route.put('/-/package/:package/dist-tags/:tag', can('publish'), media(mime.getType('json')), tag_package_version);
|
||||
route.put(
|
||||
'/-/package/:package/dist-tags/:tag',
|
||||
can('publish'),
|
||||
media(mime.getType('json')),
|
||||
tag_package_version
|
||||
);
|
||||
|
||||
route.delete('/-/package/:package/dist-tags/:tag', can('publish'), function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
route.delete('/-/package/:package/dist-tags/:tag', can('publish'), function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
const tags = {};
|
||||
tags[req.params.tag] = null;
|
||||
storage.mergeTags(req.params.package, tags, function (err: VerdaccioError): $NextFunctionVer {
|
||||
|
@ -49,7 +67,11 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler):
|
|||
});
|
||||
});
|
||||
|
||||
route.get('/-/package/:package/dist-tags', can('access'), function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
route.get('/-/package/:package/dist-tags', can('access'), function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
storage.getPackage({
|
||||
name: req.params.package,
|
||||
uplinksLook: true,
|
||||
|
@ -64,8 +86,14 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler):
|
|||
});
|
||||
});
|
||||
|
||||
route.post('/-/package/:package/dist-tags', can('publish'), function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
storage.mergeTags(req.params.package, req.body, function (err: VerdaccioError): $NextFunctionVer {
|
||||
route.post('/-/package/:package/dist-tags', can('publish'), function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
storage.mergeTags(req.params.package, req.body, function (
|
||||
err: VerdaccioError
|
||||
): $NextFunctionVer {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
import _ from 'lodash';
|
||||
import express, { Express } from 'express';
|
||||
|
||||
import { match, validateName, validatePackage, encodeScopePackage, antiLoop } from '@verdaccio/middleware';
|
||||
import { IAuth, IStorageHandler } from '@verdaccio/dev-types';
|
||||
import {
|
||||
match,
|
||||
validateName,
|
||||
validatePackage,
|
||||
encodeScopePackage,
|
||||
antiLoop,
|
||||
} from '@verdaccio/middleware';
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { IStorageHandler } from '@verdaccio/store';
|
||||
import { Config } from '@verdaccio/types';
|
||||
import bodyParser from 'body-parser';
|
||||
|
||||
|
@ -18,7 +25,11 @@ import profile from './v1/profile';
|
|||
import token from './v1/token';
|
||||
import v1Search from './v1/search';
|
||||
|
||||
export default function (config: Config, auth: IAuth, storage: IStorageHandler): Express.Application {
|
||||
export default function (
|
||||
config: Config,
|
||||
auth: IAuth,
|
||||
storage: IStorageHandler
|
||||
): Express.Application {
|
||||
/* eslint new-cap:off */
|
||||
const app = express.Router();
|
||||
/* eslint new-cap:off */
|
||||
|
|
|
@ -6,11 +6,19 @@ import { allow } from '@verdaccio/middleware';
|
|||
import { convertDistRemoteToLocalTarballUrls, getVersion, ErrorCode } from '@verdaccio/utils';
|
||||
import { HEADERS, DIST_TAGS, API_ERROR } from '@verdaccio/dev-commons';
|
||||
import { Config, Package } from '@verdaccio/types';
|
||||
import { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler } from '@verdaccio/dev-types';
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { IStorageHandler } from '@verdaccio/store';
|
||||
import { $RequestExtend, $ResponseExtend, $NextFunctionVer } from '../types/custom';
|
||||
|
||||
const debug = buildDebug('verdaccio:api:package');
|
||||
|
||||
const downloadStream = (packageName: string, filename: string, storage: any, req: $RequestExtend, res: $ResponseExtend): void => {
|
||||
const downloadStream = (
|
||||
packageName: string,
|
||||
filename: string,
|
||||
storage: any,
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend
|
||||
): void => {
|
||||
const stream = storage.getTarball(packageName, filename);
|
||||
|
||||
stream.on('content-length', function (content): void {
|
||||
|
@ -25,10 +33,19 @@ const downloadStream = (packageName: string, filename: string, storage: any, req
|
|||
stream.pipe(res);
|
||||
};
|
||||
|
||||
export default function (route: Router, auth: IAuth, storage: IStorageHandler, config: Config): void {
|
||||
export default function (
|
||||
route: Router,
|
||||
auth: IAuth,
|
||||
storage: IStorageHandler,
|
||||
config: Config
|
||||
): void {
|
||||
const can = allow(auth);
|
||||
// TODO: anonymous user?
|
||||
route.get('/:package/:version?', can('access'), function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
route.get('/:package/:version?', can('access'), function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
debug('init package by version');
|
||||
const name = req.params.package;
|
||||
const getPackageMetaCallback = function (err, metadata: Package): void {
|
||||
|
@ -81,13 +98,19 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler, c
|
|||
});
|
||||
});
|
||||
|
||||
route.get('/:scopedPackage/-/:scope/:filename', can('access'), function (req: $RequestExtend, res: $ResponseExtend): void {
|
||||
route.get('/:scopedPackage/-/:scope/:filename', can('access'), function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend
|
||||
): void {
|
||||
const { scopedPackage, filename } = req.params;
|
||||
|
||||
downloadStream(scopedPackage, filename, storage, req, res);
|
||||
});
|
||||
|
||||
route.get('/:package/-/:filename', can('access'), function (req: $RequestExtend, res: $ResponseExtend): void {
|
||||
route.get('/:package/-/:filename', can('access'), function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend
|
||||
): void {
|
||||
downloadStream(req.params.package, req.params.filename, storage, req, res);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { Router } from 'express';
|
||||
import { $RequestExtend, $ResponseExtend, $NextFunctionVer } from '@verdaccio/dev-types';
|
||||
import { $RequestExtend, $ResponseExtend, $NextFunctionVer } from '../types/custom';
|
||||
|
||||
export default function (route: Router): void {
|
||||
route.get('/-/ping', function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
route.get('/-/ping', function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
) {
|
||||
next({});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,20 +4,33 @@ import mime from 'mime';
|
|||
import { Router } from 'express';
|
||||
import buildDebug from 'debug';
|
||||
|
||||
import { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler } from '@verdaccio/dev-types';
|
||||
import { API_MESSAGE, HEADERS, DIST_TAGS, API_ERROR, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import { validateMetadata, isObject, ErrorCode, hasDiffOneKey, isRelatedToDeprecation } from '@verdaccio/utils';
|
||||
import {
|
||||
validateMetadata,
|
||||
isObject,
|
||||
ErrorCode,
|
||||
hasDiffOneKey,
|
||||
isRelatedToDeprecation,
|
||||
} from '@verdaccio/utils';
|
||||
import { media, expectJson, allow } from '@verdaccio/middleware';
|
||||
import { notify } from '@verdaccio/hooks';
|
||||
import { Config, Callback, MergeTags, Version, Package } from '@verdaccio/types';
|
||||
import { logger } from '@verdaccio/logger';
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { IStorageHandler } from '@verdaccio/store';
|
||||
import { $RequestExtend, $ResponseExtend, $NextFunctionVer } from '../types/custom';
|
||||
|
||||
import star from './star';
|
||||
import { isPublishablePackage } from './utils';
|
||||
|
||||
const debug = buildDebug('verdaccio:api:publish');
|
||||
|
||||
export default function publish(router: Router, auth: IAuth, storage: IStorageHandler, config: Config): void {
|
||||
export default function publish(
|
||||
router: Router,
|
||||
auth: IAuth,
|
||||
storage: IStorageHandler,
|
||||
config: Config
|
||||
): void {
|
||||
const can = allow(auth);
|
||||
|
||||
/**
|
||||
|
@ -79,7 +92,13 @@ export default function publish(router: Router, auth: IAuth, storage: IStorageHa
|
|||
}
|
||||
*
|
||||
*/
|
||||
router.put('/:package/:_rev?/:revision?', can('publish'), media(mime.getType('json')), expectJson, publishPackage(storage, config, auth));
|
||||
router.put(
|
||||
'/:package/:_rev?/:revision?',
|
||||
can('publish'),
|
||||
media(mime.getType('json')),
|
||||
expectJson,
|
||||
publishPackage(storage, config, auth)
|
||||
);
|
||||
|
||||
/**
|
||||
* Un-publishing an entire package.
|
||||
|
@ -92,13 +111,29 @@ export default function publish(router: Router, auth: IAuth, storage: IStorageHa
|
|||
router.delete('/:package/-rev/*', can('unpublish'), unPublishPackage(storage));
|
||||
|
||||
// removing a tarball
|
||||
router.delete('/:package/-/:filename/-rev/:revision', can('unpublish'), can('publish'), removeTarball(storage));
|
||||
router.delete(
|
||||
'/:package/-/:filename/-rev/:revision',
|
||||
can('unpublish'),
|
||||
can('publish'),
|
||||
removeTarball(storage)
|
||||
);
|
||||
|
||||
// uploading package tarball
|
||||
router.put('/:package/-/:filename/*', can('publish'), media(HEADERS.OCTET_STREAM), uploadPackageTarball(storage));
|
||||
router.put(
|
||||
'/:package/-/:filename/*',
|
||||
can('publish'),
|
||||
media(HEADERS.OCTET_STREAM),
|
||||
uploadPackageTarball(storage)
|
||||
);
|
||||
|
||||
// adding a version
|
||||
router.put('/:package/:version/-tag/:tag', can('publish'), media(mime.getType('json')), expectJson, addVersion(storage));
|
||||
router.put(
|
||||
'/:package/:version/-tag/:tag',
|
||||
can('publish'),
|
||||
media(mime.getType('json')),
|
||||
expectJson,
|
||||
addVersion(storage)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,7 +152,12 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
|||
const createTarball = function (filename: string, data, cb: Callback): void {
|
||||
const stream = storage.addTarball(packageName, filename);
|
||||
stream.on('error', function (err) {
|
||||
debug('error on stream a tarball %o for %o with error %o', filename, packageName, err.message);
|
||||
debug(
|
||||
'error on stream a tarball %o for %o with error %o',
|
||||
filename,
|
||||
packageName,
|
||||
err.message
|
||||
);
|
||||
cb(err);
|
||||
});
|
||||
stream.on('success', function () {
|
||||
|
@ -172,7 +212,11 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
|||
// npm-registry-client 0.3+ embeds tarball into the json upload
|
||||
// https://github.com/isaacs/npm-registry-client/commit/e9fbeb8b67f249394f735c74ef11fe4720d46ca0
|
||||
// issue https://github.com/rlidwka/sinopia/issues/31, dealing with it here:
|
||||
const isInvalidBodyFormat = isObject(_attachments) === false || hasDiffOneKey(_attachments) || isObject(versions) === false || hasDiffOneKey(versions);
|
||||
const isInvalidBodyFormat =
|
||||
isObject(_attachments) === false ||
|
||||
hasDiffOneKey(_attachments) ||
|
||||
isObject(versions) === false ||
|
||||
hasDiffOneKey(versions);
|
||||
|
||||
if (isInvalidBodyFormat) {
|
||||
// npm is doing something strange again
|
||||
|
@ -190,7 +234,9 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
|||
// at this point document is either created or existed before
|
||||
const [firstAttachmentKey] = Object.keys(_attachments);
|
||||
|
||||
createTarball(Path.basename(firstAttachmentKey), _attachments[firstAttachmentKey], function (error) {
|
||||
createTarball(Path.basename(firstAttachmentKey), _attachments[firstAttachmentKey], function (
|
||||
error
|
||||
) {
|
||||
debug('creating a tarball %o', firstAttachmentKey);
|
||||
if (error) {
|
||||
debug('error on create a tarball for %o with error %o', packageName, error.message);
|
||||
|
@ -199,7 +245,8 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
|||
|
||||
const versionToPublish = Object.keys(versions)[0];
|
||||
|
||||
versions[versionToPublish].readme = _.isNil(metadataCopy.readme) === false ? String(metadataCopy.readme) : '';
|
||||
versions[versionToPublish].readme =
|
||||
_.isNil(metadataCopy.readme) === false ? String(metadataCopy.readme) : '';
|
||||
|
||||
createVersion(versionToPublish, versions[versionToPublish], function (error) {
|
||||
if (error) {
|
||||
|
@ -214,7 +261,12 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
|||
}
|
||||
|
||||
try {
|
||||
await notify(metadataCopy, config, req.remote_user, `${metadataCopy.name}@${versionToPublish}`);
|
||||
await notify(
|
||||
metadataCopy,
|
||||
config,
|
||||
req.remote_user,
|
||||
`${metadataCopy.name}@${versionToPublish}`
|
||||
);
|
||||
} catch (error) {
|
||||
debug('error on notify add a new tag %o', `${metadataCopy.name}@${versionToPublish}`);
|
||||
logger.error({ error }, 'notify batch service has failed: @{error}');
|
||||
|
@ -295,14 +347,20 @@ export function removeTarball(storage: IStorageHandler) {
|
|||
const packageName = req.params.package;
|
||||
const { filename, revision } = req.params;
|
||||
|
||||
logger.debug({ packageName, filename, revision }, `removing a tarball for @{packageName}-@{tarballName}-@{revision}`);
|
||||
logger.debug(
|
||||
{ packageName, filename, revision },
|
||||
`removing a tarball for @{packageName}-@{tarballName}-@{revision}`
|
||||
);
|
||||
storage.removeTarball(packageName, filename, revision, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.status(HTTP_STATUS.CREATED);
|
||||
|
||||
logger.debug({ packageName, filename, revision }, `success remove tarball for @{packageName}-@{tarballName}-@{revision}`);
|
||||
logger.debug(
|
||||
{ packageName, filename, revision },
|
||||
`success remove tarball for @{packageName}-@{tarballName}-@{revision}`
|
||||
);
|
||||
return next({ ok: API_MESSAGE.TARBALL_REMOVED });
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import { USERS, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import { Response } from 'express';
|
||||
import _ from 'lodash';
|
||||
import { logger } from '@verdaccio/logger';
|
||||
import buildDebug from 'debug';
|
||||
|
||||
import { $RequestExtend, $NextFunctionVer, IStorageHandler } from '@verdaccio/dev-types';
|
||||
import { IStorageHandler } from '@verdaccio/store';
|
||||
import { $RequestExtend, $ResponseExtend, $NextFunctionVer } from '../types/custom';
|
||||
|
||||
const debug = buildDebug('verdaccio:api:publish:star');
|
||||
|
||||
export default function (storage: IStorageHandler): (req: $RequestExtend, res: Response, next: $NextFunctionVer) => void {
|
||||
export default function (
|
||||
storage: IStorageHandler
|
||||
): (req: $RequestExtend, res: Response, next: $NextFunctionVer) => void {
|
||||
const validateInputs = (newUsers, localUsers, username, isStar): boolean => {
|
||||
const isExistlocalUsers = _.isNil(localUsers[username]) === false;
|
||||
if (isStar && isExistlocalUsers && localUsers[username]) {
|
||||
|
@ -52,7 +54,10 @@ export default function (storage: IStorageHandler): (req: $RequestExtend, res: R
|
|||
// Check is star or unstar
|
||||
const isStar = Object.keys(newStarUser).includes(remoteUsername);
|
||||
debug('is start? %o', isStar);
|
||||
if (_.isNil(localStarUsers) === false && validateInputs(newStarUser, localStarUsers, remoteUsername, isStar)) {
|
||||
if (
|
||||
_.isNil(localStarUsers) === false &&
|
||||
validateInputs(newStarUser, localStarUsers, remoteUsername, isStar)
|
||||
) {
|
||||
return afterChangePackage();
|
||||
}
|
||||
const users = isStar
|
||||
|
|
|
@ -4,27 +4,33 @@ import { Response, Router } from 'express';
|
|||
import { USERS, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import { Package } from '@verdaccio/types';
|
||||
|
||||
import { $RequestExtend, $NextFunctionVer, IStorageHandler } from '@verdaccio/dev-types';
|
||||
import { IStorageHandler } from '@verdaccio/store';
|
||||
import { $RequestExtend, $NextFunctionVer } from '../types/custom';
|
||||
|
||||
type Packages = Package[];
|
||||
|
||||
export default function (route: Router, storage: IStorageHandler): void {
|
||||
route.get('/-/_view/starredByUser', (req: $RequestExtend, res: Response, next: $NextFunctionVer): void => {
|
||||
const remoteUsername = req.remote_user.name;
|
||||
route.get(
|
||||
'/-/_view/starredByUser',
|
||||
(req: $RequestExtend, res: Response, next: $NextFunctionVer): void => {
|
||||
const remoteUsername = req.remote_user.name;
|
||||
|
||||
storage.getLocalDatabase((err, localPackages: Packages) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
storage.getLocalDatabase((err, localPackages: Packages) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
const filteredPackages: Packages = localPackages.filter((localPackage: Package) => _.keys(localPackage[USERS]).includes(remoteUsername));
|
||||
const filteredPackages: Packages = localPackages.filter((localPackage: Package) =>
|
||||
_.keys(localPackage[USERS]).includes(remoteUsername)
|
||||
);
|
||||
|
||||
res.status(HTTP_STATUS.OK);
|
||||
next({
|
||||
rows: filteredPackages.map((filteredPackage: Package) => ({
|
||||
value: filteredPackage.name,
|
||||
})),
|
||||
res.status(HTTP_STATUS.OK);
|
||||
next({
|
||||
rows: filteredPackages.map((filteredPackage: Package) => ({
|
||||
value: filteredPackage.name,
|
||||
})),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,30 +1,50 @@
|
|||
import _ from 'lodash';
|
||||
import { Response, Router } from 'express';
|
||||
|
||||
import { createRemoteUser, getAuthenticatedMessage, validatePassword, ErrorCode } from '@verdaccio/utils';
|
||||
import {
|
||||
createRemoteUser,
|
||||
getAuthenticatedMessage,
|
||||
validatePassword,
|
||||
ErrorCode,
|
||||
} from '@verdaccio/utils';
|
||||
import { getApiToken } from '@verdaccio/auth';
|
||||
import { logger } from '@verdaccio/logger';
|
||||
|
||||
import { Config, RemoteUser } from '@verdaccio/types';
|
||||
import { $RequestExtend, $NextFunctionVer, IAuth } from '@verdaccio/dev-types';
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { API_ERROR, API_MESSAGE, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import { $RequestExtend, $NextFunctionVer } from '../types/custom';
|
||||
|
||||
export default function (route: Router, auth: IAuth, config: Config): void {
|
||||
route.get('/-/user/:org_couchdb_user', function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
route.get('/-/user/:org_couchdb_user', function (
|
||||
req: $RequestExtend,
|
||||
res: Response,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
res.status(HTTP_STATUS.OK);
|
||||
next({
|
||||
ok: getAuthenticatedMessage(req.remote_user.name),
|
||||
});
|
||||
});
|
||||
|
||||
route.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
route.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function (
|
||||
req: $RequestExtend,
|
||||
res: Response,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
const { name, password } = req.body;
|
||||
const remoteName = req.remote_user.name;
|
||||
|
||||
if (_.isNil(remoteName) === false && _.isNil(name) === false && remoteName === name) {
|
||||
auth.authenticate(name, password, async function callbackAuthenticate(err, user): Promise<void> {
|
||||
auth.authenticate(name, password, async function callbackAuthenticate(
|
||||
err,
|
||||
user
|
||||
): Promise<void> {
|
||||
if (err) {
|
||||
logger.trace({ name, err }, 'authenticating for user @{username} failed. Error: @{err.message}');
|
||||
logger.trace(
|
||||
{ name, err },
|
||||
'authenticating for user @{username} failed. Error: @{err.message}'
|
||||
);
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.UNAUTHORIZED, API_ERROR.BAD_USERNAME_PASSWORD));
|
||||
}
|
||||
|
||||
|
@ -50,12 +70,15 @@ export default function (route: Router, auth: IAuth, config: Config): void {
|
|||
// With npm registering is the same as logging in,
|
||||
// and npm accepts only an 409 error.
|
||||
// So, changing status code here.
|
||||
return next(ErrorCode.getCode(err.status, err.message) || ErrorCode.getConflict(err.message));
|
||||
return next(
|
||||
ErrorCode.getCode(err.status, err.message) || ErrorCode.getConflict(err.message)
|
||||
);
|
||||
}
|
||||
return next(err);
|
||||
}
|
||||
|
||||
const token = name && password ? await getApiToken(auth, config, user, password) : undefined;
|
||||
const token =
|
||||
name && password ? await getApiToken(auth, config, user, password) : undefined;
|
||||
|
||||
req.remote_user = user;
|
||||
res.status(HTTP_STATUS.CREATED);
|
||||
|
@ -67,7 +90,11 @@ export default function (route: Router, auth: IAuth, config: Config): void {
|
|||
}
|
||||
});
|
||||
|
||||
route.delete('/-/user/token/*', function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
route.delete('/-/user/token/*', function (
|
||||
req: $RequestExtend,
|
||||
res: Response,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
res.status(HTTP_STATUS.OK);
|
||||
next({
|
||||
ok: API_MESSAGE.LOGGED_OUT,
|
||||
|
|
|
@ -3,7 +3,8 @@ import { Response, Router } from 'express';
|
|||
|
||||
import { API_ERROR, APP_ERROR, HTTP_STATUS, SUPPORT_ERRORS } from '@verdaccio/dev-commons';
|
||||
import { ErrorCode, validatePassword } from '@verdaccio/utils';
|
||||
import { $NextFunctionVer, $RequestExtend, IAuth } from '@verdaccio/dev-types';
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { $RequestExtend, $NextFunctionVer } from '../../types/custom';
|
||||
|
||||
export interface Profile {
|
||||
tfa: boolean;
|
||||
|
@ -30,7 +31,11 @@ export default function (route: Router, auth: IAuth): void {
|
|||
};
|
||||
}
|
||||
|
||||
route.get('/-/npm/v1/user', function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
route.get('/-/npm/v1/user', function (
|
||||
req: $RequestExtend,
|
||||
res: Response,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
if (_.isNil(req.remote_user.name) === false) {
|
||||
return next(buildProfile(req.remote_user.name));
|
||||
}
|
||||
|
@ -41,7 +46,11 @@ export default function (route: Router, auth: IAuth): void {
|
|||
});
|
||||
});
|
||||
|
||||
route.post('/-/npm/v1/user', function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
route.post('/-/npm/v1/user', function (
|
||||
req: $RequestExtend,
|
||||
res: Response,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
if (_.isNil(req.remote_user.name)) {
|
||||
res.status(HTTP_STATUS.UNAUTHORIZED);
|
||||
return next({
|
||||
|
@ -65,7 +74,9 @@ export default function (route: Router, auth: IAuth): void {
|
|||
password.new,
|
||||
(err, isUpdated): $NextFunctionVer => {
|
||||
if (_.isNull(err) === false) {
|
||||
return next(ErrorCode.getCode(err.status, err.message) || ErrorCode.getConflict(err.message));
|
||||
return next(
|
||||
ErrorCode.getCode(err.status, err.message) || ErrorCode.getConflict(err.message)
|
||||
);
|
||||
}
|
||||
|
||||
if (isUpdated) {
|
||||
|
|
|
@ -40,9 +40,11 @@ function compileTextSearch(textSearch: string): (pkg: Package) => boolean {
|
|||
export default function (route, auth, storage): void {
|
||||
route.get('/-/v1/search', (req, res) => {
|
||||
// TODO: implement proper result scoring weighted by quality, popularity and maintenance query parameters
|
||||
let [text, size, from /* , quality, popularity, maintenance */] = ['text', 'size', 'from' /* , 'quality', 'popularity', 'maintenance' */].map(
|
||||
(k) => req.query[k]
|
||||
);
|
||||
let [text, size, from /* , quality, popularity, maintenance */] = [
|
||||
'text',
|
||||
'size',
|
||||
'from' /* , 'quality', 'popularity', 'maintenance' */,
|
||||
].map((k) => req.query[k]);
|
||||
|
||||
size = parseInt(size) || 20;
|
||||
from = parseInt(from) || 0;
|
||||
|
@ -61,7 +63,9 @@ export default function (route, auth, storage): void {
|
|||
return {
|
||||
package: pkg,
|
||||
flags: {
|
||||
unstable: Object.keys(pkg.versions).some((v) => semver.satisfies(v, '^1.0.0')) ? undefined : true,
|
||||
unstable: Object.keys(pkg.versions).some((v) => semver.satisfies(v, '^1.0.0'))
|
||||
? undefined
|
||||
: true,
|
||||
},
|
||||
score: {
|
||||
final: 1,
|
||||
|
|
|
@ -6,7 +6,9 @@ import { logger } from '@verdaccio/logger';
|
|||
import { Response, Router } from 'express';
|
||||
|
||||
import { Config, RemoteUser, Token } from '@verdaccio/types';
|
||||
import { $NextFunctionVer, $RequestExtend, IAuth, IStorageHandler } from '../../../types';
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { IStorageHandler } from '@verdaccio/store';
|
||||
import { $RequestExtend, $NextFunctionVer } from '../../types/custom';
|
||||
|
||||
export type NormalizeToken = Token & {
|
||||
created: string;
|
||||
|
@ -20,8 +22,17 @@ function normalizeToken(token: Token): NormalizeToken {
|
|||
}
|
||||
|
||||
// https://github.com/npm/npm-profile/blob/latest/lib/index.js
|
||||
export default function (route: Router, auth: IAuth, storage: IStorageHandler, config: Config): void {
|
||||
route.get('/-/npm/v1/tokens', async function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {
|
||||
export default function (
|
||||
route: Router,
|
||||
auth: IAuth,
|
||||
storage: IStorageHandler,
|
||||
config: Config
|
||||
): void {
|
||||
route.get('/-/npm/v1/tokens', async function (
|
||||
req: $RequestExtend,
|
||||
res: Response,
|
||||
next: $NextFunctionVer
|
||||
) {
|
||||
const { name } = req.remote_user;
|
||||
|
||||
if (_.isNil(name) === false) {
|
||||
|
@ -45,7 +56,11 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler, c
|
|||
return next(ErrorCode.getUnauthorized());
|
||||
});
|
||||
|
||||
route.post('/-/npm/v1/tokens', function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {
|
||||
route.post('/-/npm/v1/tokens', function (
|
||||
req: $RequestExtend,
|
||||
res: Response,
|
||||
next: $NextFunctionVer
|
||||
) {
|
||||
const { password, readonly, cidr_whitelist } = req.body;
|
||||
const { name } = req.remote_user;
|
||||
|
||||
|
@ -62,7 +77,9 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler, c
|
|||
req.remote_user = user;
|
||||
|
||||
if (!_.isFunction(storage.saveToken)) {
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.NOT_IMPLEMENTED, SUPPORT_ERRORS.STORAGE_NOT_IMPLEMENT));
|
||||
return next(
|
||||
ErrorCode.getCode(HTTP_STATUS.NOT_IMPLEMENTED, SUPPORT_ERRORS.STORAGE_NOT_IMPLEMENT)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -104,23 +121,26 @@ export default function (route: Router, auth: IAuth, storage: IStorageHandler, c
|
|||
});
|
||||
});
|
||||
|
||||
route.delete('/-/npm/v1/tokens/token/:tokenKey', async (req: $RequestExtend, res: Response, next: $NextFunctionVer) => {
|
||||
const {
|
||||
params: { tokenKey },
|
||||
} = req;
|
||||
const { name } = req.remote_user;
|
||||
route.delete(
|
||||
'/-/npm/v1/tokens/token/:tokenKey',
|
||||
async (req: $RequestExtend, res: Response, next: $NextFunctionVer) => {
|
||||
const {
|
||||
params: { tokenKey },
|
||||
} = req;
|
||||
const { name } = req.remote_user;
|
||||
|
||||
if (_.isNil(name) === false) {
|
||||
logger.debug({ name }, '@{name} has requested remove a token');
|
||||
try {
|
||||
await storage.deleteToken(name, tokenKey);
|
||||
logger.info({ tokenKey, name }, 'token id @{tokenKey} was revoked for user @{name}');
|
||||
return next({});
|
||||
} catch (error) {
|
||||
logger.error({ error: error.msg }, 'token creation has failed: @{error}');
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
|
||||
if (_.isNil(name) === false) {
|
||||
logger.debug({ name }, '@{name} has requested remove a token');
|
||||
try {
|
||||
await storage.deleteToken(name, tokenKey);
|
||||
logger.info({ tokenKey, name }, 'token id @{tokenKey} was revoked for user @{name}');
|
||||
return next({});
|
||||
} catch (error) {
|
||||
logger.error({ error: error.msg }, 'token creation has failed: @{error}');
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
|
||||
}
|
||||
}
|
||||
return next(ErrorCode.getUnauthorized());
|
||||
}
|
||||
return next(ErrorCode.getUnauthorized());
|
||||
});
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Response, Router } from 'express';
|
||||
import { $RequestExtend, $NextFunctionVer } from '@verdaccio/dev-types';
|
||||
import { $RequestExtend, $NextFunctionVer } from '../types/custom';
|
||||
|
||||
export default function (route: Router): void {
|
||||
route.get('/whoami', (req: $RequestExtend, res: Response, next: $NextFunctionVer): void => {
|
||||
|
|
|
@ -7,8 +7,7 @@ import { parseConfigFile } from '@verdaccio/utils';
|
|||
import { Config } from '@verdaccio/config';
|
||||
import { Storage } from '@verdaccio/store';
|
||||
import { final, handleError, errorReportingMiddleware } from '@verdaccio/middleware';
|
||||
import { Auth } from '@verdaccio/auth';
|
||||
import { IAuth } from '@verdaccio/dev-types';
|
||||
import { Auth, IAuth } from '@verdaccio/auth';
|
||||
import { HEADER_TYPE, HTTP_STATUS, generatePackageMetadata } from '@verdaccio/dev-commons';
|
||||
import { HEADERS } from '@verdaccio/commons-api';
|
||||
import apiEndpoints from '../../src';
|
||||
|
@ -56,13 +55,23 @@ export function publishVersion(app, configFile, pkgName, version): supertest.Tes
|
|||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
||||
}
|
||||
|
||||
export async function publishTaggedVersion(app, configFile, pkgName: string, version: string, tag: string) {
|
||||
export async function publishTaggedVersion(
|
||||
app,
|
||||
configFile,
|
||||
pkgName: string,
|
||||
version: string,
|
||||
tag: string
|
||||
) {
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, version, {
|
||||
[tag]: version,
|
||||
});
|
||||
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}/${encodeURIComponent(version)}/-tag/${encodeURIComponent(tag)}`)
|
||||
.put(
|
||||
`/${encodeURIComponent(pkgName)}/${encodeURIComponent(version)}/-tag/${encodeURIComponent(
|
||||
tag
|
||||
)}`
|
||||
)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(pkgMetadata))
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
|
|
|
@ -2,13 +2,15 @@ import supertest from 'supertest';
|
|||
|
||||
import { HTTP_STATUS } from '@verdaccio/commons-api';
|
||||
import { HEADER_TYPE, HEADERS } from '@verdaccio/dev-commons';
|
||||
import { $RequestExtend, $ResponseExtend } from '@verdaccio/dev-types';
|
||||
import { $ResponseExtend, $RequestExtend } from '../../types/custom';
|
||||
import { initializeServer, publishTaggedVersion, publishVersion } from './_helper';
|
||||
|
||||
const mockApiJWTmiddleware = jest.fn(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: [] };
|
||||
_next();
|
||||
});
|
||||
const mockApiJWTmiddleware = jest.fn(
|
||||
() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: [] };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
|
||||
jest.mock('@verdaccio/auth', () => ({
|
||||
Auth: class {
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
import { HTTP_STATUS } from '@verdaccio/commons-api';
|
||||
import { API_ERROR, API_MESSAGE, generatePackageMetadata, HEADER_TYPE, HEADERS } from '@verdaccio/dev-commons';
|
||||
import { $RequestExtend, $ResponseExtend } from '@verdaccio/dev-types';
|
||||
import supertest from 'supertest';
|
||||
import {
|
||||
API_ERROR,
|
||||
API_MESSAGE,
|
||||
generatePackageMetadata,
|
||||
HEADER_TYPE,
|
||||
HEADERS,
|
||||
} from '@verdaccio/dev-commons';
|
||||
import { $ResponseExtend, $RequestExtend } from '../../types/custom';
|
||||
import { initializeServer, publishVersion } from './_helper';
|
||||
|
||||
const mockApiJWTmiddleware = jest.fn(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: [] };
|
||||
_next();
|
||||
});
|
||||
const mockApiJWTmiddleware = jest.fn(
|
||||
() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: [] };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
|
||||
jest.setTimeout(50000000);
|
||||
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import supertest from 'supertest';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { HTTP_STATUS, API_ERROR } from '@verdaccio/commons-api';
|
||||
import { HEADERS, HEADER_TYPE, API_MESSAGE } from '@verdaccio/dev-commons';
|
||||
import { $RequestExtend, $ResponseExtend } from '@verdaccio/dev-types';
|
||||
import { getBadRequest, getConflict, getUnauthorized } from '@verdaccio/commons-api';
|
||||
import _ from 'lodash';
|
||||
import { $RequestExtend, $ResponseExtend } from '../../types/custom';
|
||||
import { initializeServer } from './_helper';
|
||||
|
||||
const mockApiJWTmiddleware = jest.fn(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test', groups: [], real_groups: [] };
|
||||
_next();
|
||||
});
|
||||
const mockApiJWTmiddleware = jest.fn(
|
||||
() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test', groups: [], real_groups: [] };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
|
||||
const mockAuthenticate = jest.fn(() => (_name, _password, callback): void => {
|
||||
return callback(null, ['all']);
|
||||
|
@ -42,10 +44,12 @@ describe('user', () => {
|
|||
const credentials = { name: 'test', password: 'test' };
|
||||
|
||||
test('should test add a new user', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
});
|
||||
mockApiJWTmiddleware.mockImplementationOnce(
|
||||
() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
|
||||
mockAddUser.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(null, true);
|
||||
|
@ -72,10 +76,12 @@ describe('user', () => {
|
|||
});
|
||||
|
||||
test('should test fails on add a existing user with login', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
});
|
||||
mockApiJWTmiddleware.mockImplementationOnce(
|
||||
() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put('/-/user/org.couchdb.user:jotaNew')
|
||||
.send(credentials)
|
||||
|
@ -109,10 +115,12 @@ describe('user', () => {
|
|||
});
|
||||
|
||||
test('should test fails add a new user with missing name', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
});
|
||||
mockApiJWTmiddleware.mockImplementationOnce(
|
||||
() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
mockAddUser.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(getBadRequest(API_ERROR.USERNAME_PASSWORD_REQUIRED));
|
||||
});
|
||||
|
@ -136,10 +144,12 @@ describe('user', () => {
|
|||
});
|
||||
|
||||
test('should test fails add a new user with missing password', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
});
|
||||
mockApiJWTmiddleware.mockImplementationOnce(
|
||||
() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
const credentialsShort = _.cloneDeep(credentials);
|
||||
delete credentialsShort.password;
|
||||
|
||||
|
@ -162,10 +172,12 @@ describe('user', () => {
|
|||
});
|
||||
|
||||
test('should test fails add a new user with wrong password', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test' };
|
||||
_next();
|
||||
});
|
||||
mockApiJWTmiddleware.mockImplementationOnce(
|
||||
() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test' };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
mockAuthenticate.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
|
||||
});
|
||||
|
@ -189,10 +201,12 @@ describe('user', () => {
|
|||
});
|
||||
|
||||
test('should be able to logout an user', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test' };
|
||||
_next();
|
||||
});
|
||||
mockApiJWTmiddleware.mockImplementationOnce(
|
||||
() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test' };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
mockAuthenticate.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
|
||||
});
|
||||
|
|
|
@ -2,13 +2,16 @@ import supertest from 'supertest';
|
|||
|
||||
import { HTTP_STATUS } from '@verdaccio/commons-api';
|
||||
import { HEADERS } from '@verdaccio/dev-commons';
|
||||
import { $RequestExtend, $ResponseExtend } from '@verdaccio/dev-types';
|
||||
|
||||
import { $RequestExtend, $ResponseExtend } from '../../types/custom';
|
||||
import { initializeServer } from './_helper';
|
||||
|
||||
const mockApiJWTmiddleware = jest.fn(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: [] };
|
||||
_next();
|
||||
});
|
||||
const mockApiJWTmiddleware = jest.fn(
|
||||
() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: [] };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
|
||||
jest.mock('@verdaccio/auth', () => ({
|
||||
Auth: class {
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import { HTTP_STATUS, API_ERROR } from '@verdaccio/dev-commons';
|
||||
import { addVersion, uploadPackageTarball, removeTarball, unPublishPackage, publishPackage } from '../../src/publish';
|
||||
import {
|
||||
addVersion,
|
||||
uploadPackageTarball,
|
||||
removeTarball,
|
||||
unPublishPackage,
|
||||
publishPackage,
|
||||
} from '../../src/publish';
|
||||
|
||||
const REVISION_MOCK = '15-e53a77096b0ee33e';
|
||||
|
||||
|
|
|
@ -22,9 +22,6 @@
|
|||
{
|
||||
"path": "../store"
|
||||
},
|
||||
{
|
||||
"path": "../types"
|
||||
},
|
||||
{
|
||||
"path": "../middleware"
|
||||
},
|
||||
|
|
5
packages/api/types/custom.d.ts
vendored
5
packages/api/types/custom.d.ts
vendored
|
@ -1,4 +1,9 @@
|
|||
import { Logger, RemoteUser } from '@verdaccio/types';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
|
||||
export type $RequestExtend = Request & { remote_user?: any; log: Logger };
|
||||
export type $ResponseExtend = Response & { cookies?: any };
|
||||
export type $NextFunctionVer = NextFunction & any;
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/config": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/dev-types": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/mock": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/types": "workspace:*"
|
||||
},
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import _ from 'lodash';
|
||||
import { NextFunction } from 'express';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import buildDebug from 'debug';
|
||||
|
||||
import { VerdaccioError, getBadRequest, getInternalError, getForbidden } from '@verdaccio/commons-api';
|
||||
import {
|
||||
VerdaccioError,
|
||||
getBadRequest,
|
||||
getInternalError,
|
||||
getForbidden,
|
||||
} from '@verdaccio/commons-api';
|
||||
import { API_ERROR, SUPPORT_ERRORS, TOKEN_BASIC, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
import { loadPlugin } from '@verdaccio/loaders';
|
||||
import {
|
||||
|
@ -17,15 +22,62 @@ import {
|
|||
createRemoteUser,
|
||||
} from '@verdaccio/utils';
|
||||
|
||||
import { Config, Logger, Callback, IPluginAuth, RemoteUser, JWTSignOptions, Security, AuthPluginPackage, AllowAccess, PackageAccess } from '@verdaccio/types';
|
||||
import { $RequestExtend, $ResponseExtend, IAuth, AESPayload } from '@verdaccio/dev-types';
|
||||
import { getMiddlewareCredentials, getSecurity, verifyJWTPayload, parseBasicPayload, parseAuthTokenHeader, isAuthHeaderValid, isAESLegacy } from './utils';
|
||||
import {
|
||||
Config,
|
||||
Logger,
|
||||
Callback,
|
||||
IPluginAuth,
|
||||
RemoteUser,
|
||||
IBasicAuth,
|
||||
JWTSignOptions,
|
||||
Security,
|
||||
AuthPluginPackage,
|
||||
AllowAccess,
|
||||
PackageAccess,
|
||||
} from '@verdaccio/types';
|
||||
import {
|
||||
getMiddlewareCredentials,
|
||||
getSecurity,
|
||||
verifyJWTPayload,
|
||||
parseBasicPayload,
|
||||
parseAuthTokenHeader,
|
||||
isAuthHeaderValid,
|
||||
isAESLegacy,
|
||||
} from './utils';
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const LoggerApi = require('@verdaccio/logger');
|
||||
|
||||
const debug = buildDebug('verdaccio:auth');
|
||||
|
||||
export interface IAuthWebUI {
|
||||
jwtEncrypt(user: RemoteUser, signOptions: JWTSignOptions): Promise<string>;
|
||||
aesEncrypt(buf: Buffer): Buffer;
|
||||
}
|
||||
|
||||
export interface AESPayload {
|
||||
user: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export type $RequestExtend = Request & { remote_user?: any; log: Logger };
|
||||
export type $ResponseExtend = Response & { cookies?: any };
|
||||
export type $NextFunctionVer = NextFunction & any;
|
||||
|
||||
export interface IAuthMiddleware {
|
||||
apiJWTmiddleware(): $NextFunctionVer;
|
||||
webUIJWTmiddleware(): $NextFunctionVer;
|
||||
}
|
||||
|
||||
export interface IAuth extends IBasicAuth<Config>, IAuthMiddleware, IAuthWebUI {
|
||||
config: Config;
|
||||
logger: Logger;
|
||||
secret: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
plugins: any[];
|
||||
allow_unpublish(pkg: AuthPluginPackage, user: RemoteUser, callback: Callback): void;
|
||||
}
|
||||
|
||||
class Auth implements IAuth {
|
||||
public config: Config;
|
||||
public logger: Logger;
|
||||
|
@ -46,19 +98,29 @@ class Auth implements IAuth {
|
|||
logger: this.logger,
|
||||
};
|
||||
|
||||
return loadPlugin<IPluginAuth<Config>>(config, config.auth, pluginOptions, (plugin: IPluginAuth<Config>): boolean => {
|
||||
const { authenticate, allow_access, allow_publish } = plugin;
|
||||
return loadPlugin<IPluginAuth<Config>>(
|
||||
config,
|
||||
config.auth,
|
||||
pluginOptions,
|
||||
(plugin: IPluginAuth<Config>): boolean => {
|
||||
const { authenticate, allow_access, allow_publish } = plugin;
|
||||
|
||||
// @ts-ignore
|
||||
return authenticate || allow_access || allow_publish;
|
||||
});
|
||||
// @ts-ignore
|
||||
return authenticate || allow_access || allow_publish;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _applyDefaultPlugins(): void {
|
||||
this.plugins.push(getDefaultPlugins(this.logger));
|
||||
}
|
||||
|
||||
public changePassword(username: string, password: string, newPassword: string, cb: Callback): void {
|
||||
public changePassword(
|
||||
username: string,
|
||||
password: string,
|
||||
newPassword: string,
|
||||
cb: Callback
|
||||
): void {
|
||||
const validPlugins = _.filter(this.plugins, (plugin) => isFunction(plugin.changePassword));
|
||||
|
||||
if (_.isEmpty(validPlugins)) {
|
||||
|
@ -140,7 +202,9 @@ class Auth implements IAuth {
|
|||
let method = 'adduser';
|
||||
if (isFunction(plugin[method]) === false) {
|
||||
method = 'add_user';
|
||||
self.logger.warn('the plugin method add_user in the auth plugin is deprecated and will be removed in next major release, notify to the plugin author');
|
||||
self.logger.warn(
|
||||
'the plugin method add_user in the auth plugin is deprecated and will be removed in next major release, notify to the plugin author'
|
||||
);
|
||||
}
|
||||
|
||||
if (isFunction(plugin[method]) === false) {
|
||||
|
@ -165,10 +229,18 @@ class Auth implements IAuth {
|
|||
/**
|
||||
* Allow user to access a package.
|
||||
*/
|
||||
public allow_access({ packageName, packageVersion }: AuthPluginPackage, user: RemoteUser, callback: Callback): void {
|
||||
public allow_access(
|
||||
{ packageName, packageVersion }: AuthPluginPackage,
|
||||
user: RemoteUser,
|
||||
callback: Callback
|
||||
): void {
|
||||
const plugins = this.plugins.slice(0);
|
||||
const pkgAllowAcces: AllowAccess = { name: packageName, version: packageVersion };
|
||||
const pkg = Object.assign({}, pkgAllowAcces, getMatchedPackagesSpec(packageName, this.config.packages)) as AllowAccess & PackageAccess;
|
||||
const pkg = Object.assign(
|
||||
{},
|
||||
pkgAllowAcces,
|
||||
getMatchedPackagesSpec(packageName, this.config.packages)
|
||||
) as AllowAccess & PackageAccess;
|
||||
const self = this;
|
||||
debug('allow access for %o', packageName);
|
||||
|
||||
|
@ -195,8 +267,15 @@ class Auth implements IAuth {
|
|||
})();
|
||||
}
|
||||
|
||||
public allow_unpublish({ packageName, packageVersion }: AuthPluginPackage, user: RemoteUser, callback: Callback): void {
|
||||
const pkg = Object.assign({ name: packageName, version: packageVersion }, getMatchedPackagesSpec(packageName, this.config.packages));
|
||||
public allow_unpublish(
|
||||
{ packageName, packageVersion }: AuthPluginPackage,
|
||||
user: RemoteUser,
|
||||
callback: Callback
|
||||
): void {
|
||||
const pkg = Object.assign(
|
||||
{ name: packageName, version: packageVersion },
|
||||
getMatchedPackagesSpec(packageName, this.config.packages)
|
||||
);
|
||||
debug('allow unpublish for %o', packageName);
|
||||
|
||||
for (const plugin of this.plugins) {
|
||||
|
@ -206,7 +285,10 @@ class Auth implements IAuth {
|
|||
} else {
|
||||
plugin.allow_unpublish!(user, pkg, (err, ok: boolean): void => {
|
||||
if (err) {
|
||||
debug('forbidden publish for %o, it will fallback on unpublish permissions', packageName);
|
||||
debug(
|
||||
'forbidden publish for %o, it will fallback on unpublish permissions',
|
||||
packageName
|
||||
);
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
@ -229,10 +311,17 @@ class Auth implements IAuth {
|
|||
/**
|
||||
* Allow user to publish a package.
|
||||
*/
|
||||
public allow_publish({ packageName, packageVersion }: AuthPluginPackage, user: RemoteUser, callback: Callback): void {
|
||||
public allow_publish(
|
||||
{ packageName, packageVersion }: AuthPluginPackage,
|
||||
user: RemoteUser,
|
||||
callback: Callback
|
||||
): void {
|
||||
const plugins = this.plugins.slice(0);
|
||||
const self = this;
|
||||
const pkg = Object.assign({ name: packageName, version: packageVersion }, getMatchedPackagesSpec(packageName, this.config.packages));
|
||||
const pkg = Object.assign(
|
||||
{ name: packageName, version: packageVersion },
|
||||
getMatchedPackagesSpec(packageName, this.config.packages)
|
||||
);
|
||||
debug('allow publish for %o init | plugins: %o', packageName, plugins.length);
|
||||
|
||||
(function next(): void {
|
||||
|
@ -320,7 +409,13 @@ class Auth implements IAuth {
|
|||
};
|
||||
}
|
||||
|
||||
private _handleJWTAPIMiddleware(req: $RequestExtend, security: Security, secret: string, authorization: string, next: Function): void {
|
||||
private _handleJWTAPIMiddleware(
|
||||
req: $RequestExtend,
|
||||
security: Security,
|
||||
secret: string,
|
||||
authorization: string,
|
||||
next: Function
|
||||
): void {
|
||||
const { scheme, token } = parseAuthTokenHeader(authorization);
|
||||
if (scheme.toUpperCase() === TOKEN_BASIC.toUpperCase()) {
|
||||
// this should happen when client tries to login with an existing user
|
||||
|
@ -349,7 +444,13 @@ class Auth implements IAuth {
|
|||
}
|
||||
}
|
||||
|
||||
private _handleAESMiddleware(req: $RequestExtend, security: Security, secret: string, authorization: string, next: Function): void {
|
||||
private _handleAESMiddleware(
|
||||
req: $RequestExtend,
|
||||
security: Security,
|
||||
secret: string,
|
||||
authorization: string,
|
||||
next: Function
|
||||
): void {
|
||||
const credentials: any = getMiddlewareCredentials(security, secret, authorization);
|
||||
if (credentials) {
|
||||
const { user, password } = credentials;
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export { Auth } from './auth';
|
||||
export { Auth, IAuth, IAuthWebUI } from './auth';
|
||||
export * from './utils';
|
||||
|
|
|
@ -1,8 +1,25 @@
|
|||
import { Config, RemoteUser, Security } from '@verdaccio/types';
|
||||
import { AuthMiddlewarePayload, AuthTokenHeader, BasicPayload, IAuthWebUI } from '@verdaccio/dev-types';
|
||||
import _ from 'lodash';
|
||||
import { Config, RemoteUser, Security } from '@verdaccio/types';
|
||||
import { HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
import { aesDecrypt, buildUserBuffer, convertPayloadToBase64, createAnonymousRemoteUser, defaultSecurity, ErrorCode, verifyPayload } from '@verdaccio/utils';
|
||||
import {
|
||||
aesDecrypt,
|
||||
buildUserBuffer,
|
||||
convertPayloadToBase64,
|
||||
createAnonymousRemoteUser,
|
||||
defaultSecurity,
|
||||
ErrorCode,
|
||||
verifyPayload,
|
||||
} from '@verdaccio/utils';
|
||||
|
||||
import { IAuthWebUI, AESPayload } from './auth';
|
||||
|
||||
export type BasicPayload = AESPayload | void;
|
||||
export type AuthMiddlewarePayload = RemoteUser | BasicPayload;
|
||||
|
||||
export interface AuthTokenHeader {
|
||||
scheme: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export function parseAuthTokenHeader(authorizationHeader: string): AuthTokenHeader {
|
||||
const parts = authorizationHeader.split(' ');
|
||||
|
@ -27,7 +44,11 @@ export function parseAESCredentials(authorizationHeader: string, secret: string)
|
|||
}
|
||||
}
|
||||
|
||||
export function getMiddlewareCredentials(security: Security, secret: string, authorizationHeader: string): AuthMiddlewarePayload {
|
||||
export function getMiddlewareCredentials(
|
||||
security: Security,
|
||||
secret: string,
|
||||
authorizationHeader: string
|
||||
): AuthMiddlewarePayload {
|
||||
if (isAESLegacy(security)) {
|
||||
const credentials = parseAESCredentials(authorizationHeader, secret);
|
||||
if (!credentials) {
|
||||
|
@ -54,13 +75,20 @@ export function isAESLegacy(security: Security): boolean {
|
|||
return _.isNil(legacy) === false && _.isNil(jwt) && legacy === true;
|
||||
}
|
||||
|
||||
export async function getApiToken(auth: IAuthWebUI, config: Config, remoteUser: RemoteUser, aesPassword: string): Promise<string> {
|
||||
export async function getApiToken(
|
||||
auth: IAuthWebUI,
|
||||
config: Config,
|
||||
remoteUser: RemoteUser,
|
||||
aesPassword: string
|
||||
): Promise<string> {
|
||||
const security: Security = getSecurity(config);
|
||||
|
||||
if (isAESLegacy(security)) {
|
||||
// fallback all goes to AES encryption
|
||||
return await new Promise((resolve): void => {
|
||||
resolve(auth.aesEncrypt(buildUserBuffer(remoteUser.name as string, aesPassword)).toString('base64'));
|
||||
resolve(
|
||||
auth.aesEncrypt(buildUserBuffer(remoteUser.name as string, aesPassword)).toString('base64')
|
||||
);
|
||||
});
|
||||
}
|
||||
// i am wiling to use here _.isNil but flow does not like it yet.
|
||||
|
@ -70,7 +98,9 @@ export async function getApiToken(auth: IAuthWebUI, config: Config, remoteUser:
|
|||
return await auth.jwtEncrypt(remoteUser, jwt.sign);
|
||||
}
|
||||
return await new Promise((resolve): void => {
|
||||
resolve(auth.aesEncrypt(buildUserBuffer(remoteUser.name as string, aesPassword)).toString('base64'));
|
||||
resolve(
|
||||
auth.aesEncrypt(buildUserBuffer(remoteUser.name as string, aesPassword)).toString('base64')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,8 @@ import {
|
|||
signPayload,
|
||||
} from '@verdaccio/utils';
|
||||
|
||||
import { IAuth } from '@verdaccio/dev-types';
|
||||
import { Config, Security, RemoteUser } from '@verdaccio/types';
|
||||
import { Auth } from '../src';
|
||||
import { Auth, IAuth } from '../src';
|
||||
import { getMiddlewareCredentials, getApiToken, verifyJWTPayload, getSecurity } from '../src';
|
||||
|
||||
setup([]);
|
||||
|
@ -86,7 +85,9 @@ describe('Auth utilities', () => {
|
|||
};
|
||||
|
||||
const verifyAES = (token: string, user: string, password: string, secret: string) => {
|
||||
const payload = aesDecrypt(convertPayloadToBase64(token), secret).toString(CHARACTER_ENCODING.UTF8);
|
||||
const payload = aesDecrypt(convertPayloadToBase64(token), secret).toString(
|
||||
CHARACTER_ENCODING.UTF8
|
||||
);
|
||||
const content = payload.split(':');
|
||||
|
||||
expect(content[0]).toBe(user);
|
||||
|
@ -95,49 +96,98 @@ describe('Auth utilities', () => {
|
|||
|
||||
describe('getApiToken test', () => {
|
||||
test('should sign token with aes and security missing', async () => {
|
||||
const token = await signCredentials('security-missing', 'test', 'test', '1234567', 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-missing',
|
||||
'test',
|
||||
'test',
|
||||
'1234567',
|
||||
'aesEncrypt',
|
||||
'jwtEncrypt'
|
||||
);
|
||||
|
||||
verifyAES(token, 'test', 'test', '1234567');
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should sign token with aes and security empty', async () => {
|
||||
const token = await signCredentials('security-empty', 'test', 'test', '123456', 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-empty',
|
||||
'test',
|
||||
'test',
|
||||
'123456',
|
||||
'aesEncrypt',
|
||||
'jwtEncrypt'
|
||||
);
|
||||
|
||||
verifyAES(token, 'test', 'test', '123456');
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should sign token with aes', async () => {
|
||||
const token = await signCredentials('security-basic', 'test', 'test', '123456', 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-basic',
|
||||
'test',
|
||||
'test',
|
||||
'123456',
|
||||
'aesEncrypt',
|
||||
'jwtEncrypt'
|
||||
);
|
||||
|
||||
verifyAES(token, 'test', 'test', '123456');
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should sign token with legacy and jwt disabled', async () => {
|
||||
const token = await signCredentials('security-no-legacy', 'test', 'test', 'x8T#ZCx=2t', 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-no-legacy',
|
||||
'test',
|
||||
'test',
|
||||
'x8T#ZCx=2t',
|
||||
'aesEncrypt',
|
||||
'jwtEncrypt'
|
||||
);
|
||||
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
verifyAES(token, 'test', 'test', 'x8T#ZCx=2t');
|
||||
});
|
||||
|
||||
test('should sign token with legacy enabled and jwt enabled', async () => {
|
||||
const token = await signCredentials('security-jwt-legacy-enabled', 'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-jwt-legacy-enabled',
|
||||
'test',
|
||||
'test',
|
||||
'secret',
|
||||
'jwtEncrypt',
|
||||
'aesEncrypt'
|
||||
);
|
||||
|
||||
verifyJWT(token, 'test', 'test', 'secret');
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should sign token with jwt enabled', async () => {
|
||||
const token = await signCredentials('security-jwt', 'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-jwt',
|
||||
'test',
|
||||
'test',
|
||||
'secret',
|
||||
'jwtEncrypt',
|
||||
'aesEncrypt'
|
||||
);
|
||||
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
verifyJWT(token, 'test', 'test', 'secret');
|
||||
});
|
||||
|
||||
test('should sign with jwt whether legacy is disabled', async () => {
|
||||
const token = await signCredentials('security-legacy-disabled', 'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-legacy-disabled',
|
||||
'test',
|
||||
'test',
|
||||
'secret',
|
||||
'jwtEncrypt',
|
||||
'aesEncrypt'
|
||||
);
|
||||
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
verifyJWT(token, 'test', 'test', 'secret');
|
||||
|
@ -156,7 +206,14 @@ describe('Auth utilities', () => {
|
|||
const secret = 'secret';
|
||||
const user = 'test';
|
||||
const pass = 'test';
|
||||
const token = await signCredentials('security-legacy', user, pass, secret, 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-legacy',
|
||||
user,
|
||||
pass,
|
||||
secret,
|
||||
'aesEncrypt',
|
||||
'jwtEncrypt'
|
||||
);
|
||||
const config: Config = getConfig('security-legacy', secret);
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, secret, `Bearer ${token}`);
|
||||
|
@ -184,19 +241,41 @@ describe('Auth utilities', () => {
|
|||
|
||||
test.concurrent('should return empty credential wrong secret key', async () => {
|
||||
const secret = 'secret';
|
||||
const token = await signCredentials('security-legacy', 'test', 'test', secret, 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-legacy',
|
||||
'test',
|
||||
'test',
|
||||
secret,
|
||||
'aesEncrypt',
|
||||
'jwtEncrypt'
|
||||
);
|
||||
const config: Config = getConfig('security-legacy', secret);
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, 'BAD_SECRET', buildToken(TOKEN_BEARER, token));
|
||||
const credentials = getMiddlewareCredentials(
|
||||
security,
|
||||
'BAD_SECRET',
|
||||
buildToken(TOKEN_BEARER, token)
|
||||
);
|
||||
expect(credentials).not.toBeDefined();
|
||||
});
|
||||
|
||||
test.concurrent('should return empty credential wrong scheme', async () => {
|
||||
const secret = 'secret';
|
||||
const token = await signCredentials('security-legacy', 'test', 'test', secret, 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-legacy',
|
||||
'test',
|
||||
'test',
|
||||
secret,
|
||||
'aesEncrypt',
|
||||
'jwtEncrypt'
|
||||
);
|
||||
const config: Config = getConfig('security-legacy', secret);
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, secret, buildToken('BAD_SCHEME', token));
|
||||
const credentials = getMiddlewareCredentials(
|
||||
security,
|
||||
secret,
|
||||
buildToken('BAD_SCHEME', token)
|
||||
);
|
||||
expect(credentials).not.toBeDefined();
|
||||
});
|
||||
|
||||
|
@ -206,7 +285,11 @@ describe('Auth utilities', () => {
|
|||
const auth: IAuth = new Auth(config);
|
||||
const token = auth.aesEncrypt(Buffer.from(`corruptedBuffer`)).toString('base64');
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, secret, buildToken(TOKEN_BEARER, token));
|
||||
const credentials = getMiddlewareCredentials(
|
||||
security,
|
||||
secret,
|
||||
buildToken(TOKEN_BEARER, token)
|
||||
);
|
||||
expect(credentials).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
|
@ -229,7 +312,11 @@ describe('Auth utilities', () => {
|
|||
test('should return anonymous whether token is corrupted', () => {
|
||||
const config: Config = getConfig('security-jwt', '12345');
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, '12345', buildToken(TOKEN_BEARER, 'fakeToken'));
|
||||
const credentials = getMiddlewareCredentials(
|
||||
security,
|
||||
'12345',
|
||||
buildToken(TOKEN_BEARER, 'fakeToken')
|
||||
);
|
||||
|
||||
expect(credentials).toBeDefined();
|
||||
// @ts-ignore
|
||||
|
@ -243,7 +330,11 @@ describe('Auth utilities', () => {
|
|||
test('should return anonymous whether token and scheme are corrupted', () => {
|
||||
const config: Config = getConfig('security-jwt', '12345');
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, '12345', buildToken('FakeScheme', 'fakeToken'));
|
||||
const credentials = getMiddlewareCredentials(
|
||||
security,
|
||||
'12345',
|
||||
buildToken('FakeScheme', 'fakeToken')
|
||||
);
|
||||
|
||||
expect(credentials).not.toBeDefined();
|
||||
});
|
||||
|
@ -252,9 +343,20 @@ describe('Auth utilities', () => {
|
|||
const secret = 'secret';
|
||||
const user = 'test';
|
||||
const config: Config = getConfig('security-jwt', secret);
|
||||
const token = await signCredentials('security-jwt', user, 'secretTest', secret, 'jwtEncrypt', 'aesEncrypt');
|
||||
const token = await signCredentials(
|
||||
'security-jwt',
|
||||
user,
|
||||
'secretTest',
|
||||
secret,
|
||||
'jwtEncrypt',
|
||||
'aesEncrypt'
|
||||
);
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, secret, buildToken(TOKEN_BEARER, token));
|
||||
const credentials = getMiddlewareCredentials(
|
||||
security,
|
||||
secret,
|
||||
buildToken(TOKEN_BEARER, token)
|
||||
);
|
||||
expect(credentials).toBeDefined();
|
||||
// @ts-ignore
|
||||
expect(credentials.name).toEqual(user);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'lodash';
|
||||
import { Config as AppConfig } from '@verdaccio/config';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
import { IAuth } from '@verdaccio/dev-types';
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { Config } from '@verdaccio/types';
|
||||
import { ROLES } from '@verdaccio/dev-commons';
|
||||
import { getInternalError } from '@verdaccio/commons-api';
|
||||
|
@ -35,7 +35,14 @@ describe('AuthTest', () => {
|
|||
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
expect(callback).toHaveBeenCalledWith(null, {
|
||||
groups: ['test', ROLES.$ALL, ROLES.$AUTH, ROLES.DEPRECATED_ALL, ROLES.DEPRECATED_AUTH, ROLES.ALL],
|
||||
groups: [
|
||||
'test',
|
||||
ROLES.$ALL,
|
||||
ROLES.$AUTH,
|
||||
ROLES.DEPRECATED_ALL,
|
||||
ROLES.DEPRECATED_AUTH,
|
||||
ROLES.ALL,
|
||||
],
|
||||
name: 'foo',
|
||||
real_groups: groups,
|
||||
});
|
||||
|
|
|
@ -13,11 +13,19 @@ import { isVersionValid, MIN_NODE_VERSION } from './utils';
|
|||
const isRootUser = process.getuid && process.getuid() === 0;
|
||||
|
||||
if (isRootUser) {
|
||||
global.console.warn(bgYellow().red("*** WARNING: Verdaccio doesn't need superuser privileges. Don't run it under root! ***"));
|
||||
global.console.warn(
|
||||
bgYellow().red(
|
||||
"*** WARNING: Verdaccio doesn't need superuser privileges. Don't run it under root! ***"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (isVersionValid()) {
|
||||
global.console.error(bgRed(`Verdaccio requires at least Node.js ${MIN_NODE_VERSION} or higher, please upgrade your Node.js distribution`));
|
||||
global.console.error(
|
||||
bgRed(
|
||||
`Verdaccio requires at least Node.js ${MIN_NODE_VERSION} or higher, please upgrade your Node.js distribution`
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,14 @@ export default function initProgram(commander, pkgVersion, pkgName) {
|
|||
|
||||
// initLogger.warn({file: configPathLocation}, 'config file - @{file}');
|
||||
|
||||
startVerdaccio(verdaccioConfiguration, cliListener, configPathLocation, pkgVersion, pkgName, listenDefaultCallback);
|
||||
startVerdaccio(
|
||||
verdaccioConfiguration,
|
||||
cliListener,
|
||||
configPathLocation,
|
||||
pkgVersion,
|
||||
pkgName,
|
||||
listenDefaultCallback
|
||||
);
|
||||
} catch (err) {
|
||||
// initLogger.fatal({file: configPathLocation, err: err}, 'cannot open config file @{file}: @{!err.message}');
|
||||
process.exit(1);
|
||||
|
|
|
@ -2,4 +2,5 @@ import semver from 'semver';
|
|||
|
||||
export const MIN_NODE_VERSION = '6.9.0';
|
||||
|
||||
export const isVersionValid = () => semver.satisfies(process.version, `>=${MIN_NODE_VERSION}`) === false;
|
||||
export const isVersionValid = () =>
|
||||
semver.satisfies(process.version, `>=${MIN_NODE_VERSION}`) === false;
|
||||
|
|
|
@ -162,6 +162,7 @@ export const STORAGE = {
|
|||
DEFAULT_REVISION: '0-0000000000000000'
|
||||
};
|
||||
|
||||
export const LOG_STATUS_MESSAGE = "@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}'";
|
||||
export const LOG_STATUS_MESSAGE =
|
||||
"@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}'";
|
||||
export const LOG_VERDACCIO_ERROR = `${LOG_STATUS_MESSAGE}, error: @{!error}`;
|
||||
export const LOG_VERDACCIO_BYTES = `${LOG_STATUS_MESSAGE}, bytes: @{bytes.in}/@{bytes.out}`;
|
||||
|
|
|
@ -4,7 +4,11 @@ export interface DistTags {
|
|||
[key: string]: string;
|
||||
}
|
||||
|
||||
export function generatePackageMetadata(pkgName: string, version = '1.0.0', distTags: DistTags = { ['latest']: version }): Package {
|
||||
export function generatePackageMetadata(
|
||||
pkgName: string,
|
||||
version = '1.0.0',
|
||||
distTags: DistTags = { ['latest']: version }
|
||||
): Package {
|
||||
// @ts-ignore
|
||||
return {
|
||||
_id: pkgName,
|
||||
|
@ -38,7 +42,8 @@ export function generatePackageMetadata(pkgName: string, version = '1.0.0', dist
|
|||
name: 'foo',
|
||||
},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
integrity:
|
||||
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
||||
tarball: `http:\/\/localhost:5555\/${pkgName}\/-\/${pkgName}-${version}.tgz`,
|
||||
},
|
||||
|
|
|
@ -22,9 +22,6 @@
|
|||
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
|
||||
"build": "pnpm run build:js && pnpm run build:types"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/dev-types": "workspace:5.0.0-alpha.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@verdaccio/dev-commons": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/logger": "workspace:5.0.0-alpha.0",
|
||||
|
|
|
@ -37,7 +37,9 @@ function findConfigFile(configPath: string): string {
|
|||
throw new Error('no configuration files can be processed');
|
||||
}
|
||||
|
||||
const primaryConf: any = _.find(configPaths, (configLocation: any) => fileExists(configLocation.path));
|
||||
const primaryConf: any = _.find(configPaths, (configLocation: any) =>
|
||||
fileExists(configLocation.path)
|
||||
);
|
||||
if (_.isNil(primaryConf) === false) {
|
||||
return primaryConf.path;
|
||||
}
|
||||
|
@ -75,7 +77,8 @@ function updateStorageLinks(configLocation, defaultConfig): string {
|
|||
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored,
|
||||
// If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
|
||||
// $FlowFixMe
|
||||
let dataDir = process.env.XDG_DATA_HOME || Path.join(process.env.HOME as string, '.local', 'share');
|
||||
let dataDir =
|
||||
process.env.XDG_DATA_HOME || Path.join(process.env.HOME as string, '.local', 'share');
|
||||
if (folderExists(dataDir)) {
|
||||
dataDir = Path.resolve(Path.join(dataDir, pkgJSON.name, 'storage'));
|
||||
return defaultConfig.replace(/^storage: .\/storage$/m, `storage: ${dataDir}`);
|
||||
|
@ -84,16 +87,17 @@ function updateStorageLinks(configLocation, defaultConfig): string {
|
|||
}
|
||||
|
||||
function getConfigPaths(): SetupDirectory[] {
|
||||
const listPaths: SetupDirectory[] = [getXDGDirectory(), getWindowsDirectory(), getRelativeDefaultDirectory(), getOldDirectory()].reduce(function (
|
||||
acc,
|
||||
currentValue: any
|
||||
): SetupDirectory[] {
|
||||
const listPaths: SetupDirectory[] = [
|
||||
getXDGDirectory(),
|
||||
getWindowsDirectory(),
|
||||
getRelativeDefaultDirectory(),
|
||||
getOldDirectory(),
|
||||
].reduce(function (acc, currentValue: any): SetupDirectory[] {
|
||||
if (_.isUndefined(currentValue) === false) {
|
||||
acc.push(currentValue);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[] as SetupDirectory[]);
|
||||
}, [] as SetupDirectory[]);
|
||||
|
||||
return listPaths;
|
||||
}
|
||||
|
|
|
@ -12,15 +12,27 @@ import {
|
|||
} from '@verdaccio/utils';
|
||||
import { APP_ERROR } from '@verdaccio/dev-commons';
|
||||
|
||||
import { PackageList, Config as AppConfig, Security, Logger } from '@verdaccio/types';
|
||||
|
||||
import { MatchedPackage, StartUpConfig } from '@verdaccio/dev-types';
|
||||
import {
|
||||
PackageList,
|
||||
Config as AppConfig,
|
||||
Security,
|
||||
Logger,
|
||||
PackageAccess,
|
||||
} from '@verdaccio/types';
|
||||
|
||||
const LoggerApi = require('@verdaccio/logger');
|
||||
|
||||
const strategicConfigProps = ['uplinks', 'packages'];
|
||||
const allowedEnvConfig = ['http_proxy', 'https_proxy', 'no_proxy'];
|
||||
|
||||
export type MatchedPackage = PackageAccess | void;
|
||||
|
||||
export interface StartUpConfig {
|
||||
storage: string;
|
||||
plugins?: string;
|
||||
self_path: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coordinates the application configuration
|
||||
*/
|
||||
|
|
|
@ -113,7 +113,9 @@ export function getBadRequest(customMessage: string): VerdaccioError {
|
|||
}
|
||||
|
||||
export function getInternalError(customMessage?: string): VerdaccioError {
|
||||
return customMessage ? getError(HTTP_STATUS.INTERNAL_ERROR, customMessage) : getError(HTTP_STATUS.INTERNAL_ERROR, API_ERROR.UNKNOWN_ERROR);
|
||||
return customMessage
|
||||
? getError(HTTP_STATUS.INTERNAL_ERROR, customMessage)
|
||||
: getError(HTTP_STATUS.INTERNAL_ERROR, API_ERROR.UNKNOWN_ERROR);
|
||||
}
|
||||
|
||||
export function getUnauthorized(message = 'no credentials provided'): VerdaccioError {
|
||||
|
@ -124,7 +126,9 @@ export function getForbidden(message = "can't use this filename"): VerdaccioErro
|
|||
return getError(HTTP_STATUS.FORBIDDEN, message);
|
||||
}
|
||||
|
||||
export function getServiceUnavailable(message: string = API_ERROR.RESOURCE_UNAVAILABLE): VerdaccioError {
|
||||
export function getServiceUnavailable(
|
||||
message: string = API_ERROR.RESOURCE_UNAVAILABLE
|
||||
): VerdaccioError {
|
||||
return getError(HTTP_STATUS.SERVICE_UNAVAILABLE, message);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,11 @@ export type ReadFileOptions = {
|
|||
* @param {*} callback
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
function readFile(name: string, options: ReadFileOptions = {}, callback: Callback = (): void => {}): void {
|
||||
function readFile(
|
||||
name: string,
|
||||
options: ReadFileOptions = {},
|
||||
callback: Callback = (): void => {}
|
||||
): void {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
|
|
|
@ -32,7 +32,9 @@ describe('testing locking', () => {
|
|||
|
||||
test('file should fail to be found to be locked', (done) => {
|
||||
lockFile(getFilePath('package.fail.json'), (error: Error) => {
|
||||
expect(error.message).toMatch(/ENOENT: no such file or directory, stat '(.*)package.fail.json'/);
|
||||
expect(error.message).toMatch(
|
||||
/ENOENT: no such file or directory, stat '(.*)package.fail.json'/
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -83,7 +85,9 @@ describe('testing locking', () => {
|
|||
parse: true,
|
||||
};
|
||||
readFile(getFilePath('package.fail.json'), options, (error: Error) => {
|
||||
expect(error.message).toMatch(/ENOENT: no such file or directory, open '(.*)package.fail.json'/);
|
||||
expect(error.message).toMatch(
|
||||
/ENOENT: no such file or directory, open '(.*)package.fail.json'/
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,14 @@ import Path from 'path';
|
|||
import { Callback, AuthConf, Config, IPluginAuth } from '@verdaccio/types';
|
||||
import { unlockFile } from '@verdaccio/file-locking';
|
||||
|
||||
import { verifyPassword, lockAndRead, parseHTPasswd, addUserToHTPasswd, changePasswordToHTPasswd, sanityCheck } from './utils';
|
||||
import {
|
||||
verifyPassword,
|
||||
lockAndRead,
|
||||
parseHTPasswd,
|
||||
addUserToHTPasswd,
|
||||
changePasswordToHTPasswd,
|
||||
sanityCheck,
|
||||
} from './utils';
|
||||
|
||||
export interface VerdaccioConfigApp extends Config {
|
||||
file: string;
|
||||
|
@ -197,7 +204,12 @@ export default class HTPasswd implements IPluginAuth<VerdaccioConfigApp> {
|
|||
* @param {function} cd
|
||||
* @returns {function}
|
||||
*/
|
||||
public changePassword(user: string, password: string, newPassword: string, realCb: Callback): void {
|
||||
public changePassword(
|
||||
user: string,
|
||||
password: string,
|
||||
newPassword: string,
|
||||
realCb: Callback
|
||||
): void {
|
||||
lockAndRead(this.path, (err, res) => {
|
||||
let locked = false;
|
||||
const pathPassFile = this.path;
|
||||
|
|
|
@ -95,7 +95,13 @@ export function addUserToHTPasswd(body: string, user: string, passwd: string): s
|
|||
* @param {number} maxUsers
|
||||
* @returns {object}
|
||||
*/
|
||||
export function sanityCheck(user: string, password: string, verifyFn: Callback, users: {}, maxUsers: number): HttpError | null {
|
||||
export function sanityCheck(
|
||||
user: string,
|
||||
password: string,
|
||||
verifyFn: Callback,
|
||||
users: {},
|
||||
maxUsers: number
|
||||
): HttpError | null {
|
||||
let err;
|
||||
|
||||
// check for user or password
|
||||
|
@ -144,7 +150,12 @@ export function getCryptoPassword(password: string): string {
|
|||
* @param {string} newPasswd
|
||||
* @returns {string}
|
||||
*/
|
||||
export function changePasswordToHTPasswd(body: string, user: string, passwd: string, newPasswd: string): string {
|
||||
export function changePasswordToHTPasswd(
|
||||
body: string,
|
||||
user: string,
|
||||
passwd: string,
|
||||
newPasswd: string
|
||||
): string {
|
||||
let lines = body.split('\n');
|
||||
lines = lines.map((line) => {
|
||||
const [username, password] = line.split(':', 3);
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
import crypto from 'crypto';
|
||||
|
||||
import { verifyPassword, lockAndRead, parseHTPasswd, addUserToHTPasswd, sanityCheck, changePasswordToHTPasswd, getCryptoPassword } from '../src/utils';
|
||||
import {
|
||||
verifyPassword,
|
||||
lockAndRead,
|
||||
parseHTPasswd,
|
||||
addUserToHTPasswd,
|
||||
sanityCheck,
|
||||
changePasswordToHTPasswd,
|
||||
getCryptoPassword,
|
||||
} from '../src/utils';
|
||||
|
||||
const mockReadFile = jest.fn();
|
||||
const mockUnlockFile = jest.fn();
|
||||
|
@ -69,7 +77,10 @@ describe('verifyPassword', () => {
|
|||
expect(verifyPassword(input[0], input[1])).toBeTruthy();
|
||||
});
|
||||
it('should verify the bcrypt password with false', () => {
|
||||
const input = ['testpasswordchanged', '$2y$04$Wqed4yN0OktGbiUdxSTwtOva1xfESfkNIZfcS9/vmHLsn3.lkFxJO'];
|
||||
const input = [
|
||||
'testpasswordchanged',
|
||||
'$2y$04$Wqed4yN0OktGbiUdxSTwtOva1xfESfkNIZfcS9/vmHLsn3.lkFxJO',
|
||||
];
|
||||
expect(verifyPassword(input[0], input[1])).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
@ -116,7 +127,9 @@ describe('addUserToHTPasswd - crypto', () => {
|
|||
jest.doMock('../src/crypt3.ts', () => false);
|
||||
const input = ['', 'username', 'password'];
|
||||
const utils = require('../src/utils.ts');
|
||||
expect(utils.addUserToHTPasswd(input[0], input[1], input[2])).toEqual('username:{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=:autocreated 2018-01-14T11:17:40.712Z\n');
|
||||
expect(utils.addUserToHTPasswd(input[0], input[1], input[2])).toEqual(
|
||||
'username:{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=:autocreated 2018-01-14T11:17:40.712Z\n'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -229,7 +242,9 @@ describe('changePasswordToHTPasswd', () => {
|
|||
|
||||
const body = 'root:$6qLTHoPfGLy2:autocreated 2018-08-20T13:38:12.164Z';
|
||||
|
||||
expect(changePasswordToHTPasswd(body, 'root', 'demo123', 'demo123')).toEqual('root:ABfaAAjDKIgfw:autocreated 2018-08-20T13:38:12.164Z');
|
||||
expect(changePasswordToHTPasswd(body, 'root', 'demo123', 'demo123')).toEqual(
|
||||
'root:ABfaAAjDKIgfw:autocreated 2018-08-20T13:38:12.164Z'
|
||||
);
|
||||
});
|
||||
|
||||
test('should change the password when crypt3 is not available', () => {
|
||||
|
|
|
@ -6,7 +6,17 @@ import buildDebug from 'debug';
|
|||
import _ from 'lodash';
|
||||
import async from 'async';
|
||||
import mkdirp from 'mkdirp';
|
||||
import { Callback, Config, IPackageStorage, IPluginStorage, LocalStorage, Logger, StorageList, Token, TokenFilter } from '@verdaccio/types';
|
||||
import {
|
||||
Callback,
|
||||
Config,
|
||||
IPackageStorage,
|
||||
IPluginStorage,
|
||||
LocalStorage,
|
||||
Logger,
|
||||
StorageList,
|
||||
Token,
|
||||
TokenFilter,
|
||||
} from '@verdaccio/types';
|
||||
import level from 'level';
|
||||
import { getInternalError } from '@verdaccio/commons-api';
|
||||
|
||||
|
@ -82,7 +92,11 @@ class LocalDatabase implements IPluginStorage<{}> {
|
|||
}
|
||||
}
|
||||
|
||||
public search(onPackage: Callback, onEnd: Callback, validateName: (name: string) => boolean): void {
|
||||
public search(
|
||||
onPackage: Callback,
|
||||
onEnd: Callback,
|
||||
validateName: (name: string) => boolean
|
||||
): void {
|
||||
const storages = this._getCustomPackageLocalStorages();
|
||||
debug(`search custom local packages: %o`, JSON.stringify(storages));
|
||||
const base = Path.dirname(this.config.self_path);
|
||||
|
@ -182,7 +196,10 @@ class LocalDatabase implements IPluginStorage<{}> {
|
|||
this.get((err, data) => {
|
||||
if (err) {
|
||||
cb(getInternalError('error remove private package'));
|
||||
this.logger.error({ err }, '[local-storage/remove]: remove the private package has failed @{err}');
|
||||
this.logger.error(
|
||||
{ err },
|
||||
'[local-storage/remove]: remove the private package has failed @{err}'
|
||||
);
|
||||
debug('error on remove package %o', name);
|
||||
}
|
||||
|
||||
|
@ -213,7 +230,9 @@ class LocalDatabase implements IPluginStorage<{}> {
|
|||
public getPackageStorage(packageName: string): IPackageStorage {
|
||||
const packageAccess = this.config.getMatchedPackagesSpec(packageName);
|
||||
|
||||
const packagePath: string = this._getLocalStoragePath(packageAccess ? packageAccess.storage : undefined);
|
||||
const packagePath: string = this._getLocalStoragePath(
|
||||
packageAccess ? packageAccess.storage : undefined
|
||||
);
|
||||
debug('storage path selected: ', packagePath);
|
||||
|
||||
if (_.isString(packagePath) === false) {
|
||||
|
@ -221,7 +240,10 @@ class LocalDatabase implements IPluginStorage<{}> {
|
|||
return;
|
||||
}
|
||||
|
||||
const packageStoragePath: string = Path.join(Path.resolve(Path.dirname(this.config.self_path || ''), packagePath), packageName);
|
||||
const packageStoragePath: string = Path.join(
|
||||
Path.resolve(Path.dirname(this.config.self_path || ''), packagePath),
|
||||
packageName
|
||||
);
|
||||
|
||||
debug('storage absolute path: ', packageStoragePath);
|
||||
|
||||
|
@ -317,8 +339,12 @@ class LocalDatabase implements IPluginStorage<{}> {
|
|||
debug('sync database started');
|
||||
|
||||
if (this.locked) {
|
||||
this.logger.error('Database is locked, please check error message printed during startup to prevent data loss.');
|
||||
return new Error('Verdaccio database is locked, please contact your administrator to checkout logs during verdaccio startup.');
|
||||
this.logger.error(
|
||||
'Database is locked, please check error message printed during startup to prevent data loss.'
|
||||
);
|
||||
return new Error(
|
||||
'Verdaccio database is locked, please contact your administrator to checkout logs during verdaccio startup.'
|
||||
);
|
||||
}
|
||||
// Uses sync to prevent ugly race condition
|
||||
try {
|
||||
|
@ -383,7 +409,9 @@ class LocalDatabase implements IPluginStorage<{}> {
|
|||
}
|
||||
|
||||
private _dbGenPath(dbName: string, config: Config): string {
|
||||
return Path.join(Path.resolve(Path.dirname(config.self_path || ''), config.storage as string, dbName));
|
||||
return Path.join(
|
||||
Path.resolve(Path.dirname(config.self_path || ''), config.storage as string, dbName)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -404,7 +432,10 @@ class LocalDatabase implements IPluginStorage<{}> {
|
|||
// Only recreate if file not found to prevent data loss
|
||||
if (err.code !== noSuchFile) {
|
||||
this.locked = true;
|
||||
this.logger.error('Failed to read package database file, please check the error printed below:\n', `File Path: ${this.path}\n\n ${err.message}`);
|
||||
this.logger.error(
|
||||
'Failed to read package database file, please check the error printed below:\n',
|
||||
`File Path: ${this.path}\n\n ${err.message}`
|
||||
);
|
||||
}
|
||||
|
||||
return emptyDatabase;
|
||||
|
|
|
@ -78,7 +78,13 @@ export default class LocalFS implements ILocalFSPackageManager {
|
|||
* @param {*} transformPackage
|
||||
* @param {*} onEnd
|
||||
*/
|
||||
public updatePackage(name: string, updateHandler: Callback, onWrite: Callback, transformPackage: Function, onEnd: Callback): void {
|
||||
public updatePackage(
|
||||
name: string,
|
||||
updateHandler: Callback,
|
||||
onWrite: Callback,
|
||||
transformPackage: Function,
|
||||
onEnd: Callback
|
||||
): void {
|
||||
this._lockAndReadJSON(pkgFileName, (err, json) => {
|
||||
let locked = false;
|
||||
const self = this;
|
||||
|
@ -127,7 +133,10 @@ export default class LocalFS implements ILocalFSPackageManager {
|
|||
});
|
||||
}
|
||||
|
||||
public deletePackage(packageName: string, callback: (err: NodeJS.ErrnoException | null) => void): void {
|
||||
public deletePackage(
|
||||
packageName: string,
|
||||
callback: (err: NodeJS.ErrnoException | null) => void
|
||||
): void {
|
||||
debug('delete a package %o', packageName);
|
||||
|
||||
return fs.unlink(this._getStorage(packageName), callback);
|
||||
|
@ -190,7 +199,10 @@ export default class LocalFS implements ILocalFSPackageManager {
|
|||
if (exists) {
|
||||
uploadStream.emit('error', fSError(fileExist));
|
||||
} else {
|
||||
const temporalName = path.join(this.path, `${name}.tmp-${String(Math.random()).replace(/^0\./, '')}`);
|
||||
const temporalName = path.join(
|
||||
this.path,
|
||||
`${name}.tmp-${String(Math.random()).replace(/^0\./, '')}`
|
||||
);
|
||||
debug('write a temporal name %o', temporalName);
|
||||
const file = fs.createWriteStream(temporalName);
|
||||
const removeTempFile = (): void => fs.unlink(temporalName, () => {});
|
||||
|
|
|
@ -17,7 +17,10 @@ export function loadPrivatePackages(path: string, logger: Logger): LocalStorage
|
|||
try {
|
||||
db = JSON.parse(data);
|
||||
} catch (err) {
|
||||
logger.error(`Package database file corrupted (invalid JSON), please check the error printed below.\nFile Path: ${path}`, err);
|
||||
logger.error(
|
||||
`Package database file corrupted (invalid JSON), please check the error printed below.\nFile Path: ${path}`,
|
||||
err
|
||||
);
|
||||
throw Error('Package database file corrupted (invalid JSON)');
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,10 @@ function hasScope(file: string): boolean {
|
|||
}
|
||||
|
||||
/* eslint-disable no-async-promise-executor */
|
||||
export async function findPackages(storagePath: string, validationHandler: Function): Promise<{ name: string; path: string }[]> {
|
||||
export async function findPackages(
|
||||
storagePath: string,
|
||||
validationHandler: Function
|
||||
): Promise<{ name: string; path: string }[]> {
|
||||
const listPackages: { name: string; path: string }[] = [];
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
|
|
|
@ -30,7 +30,8 @@ const json = {
|
|||
_nodeVersion: '8.7.0',
|
||||
_npmUser: {},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
integrity:
|
||||
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9',
|
||||
tarball: 'http://localhost:5555/@scope/pk1-test/-/@scope/pk1-test-1.0.6.tgz',
|
||||
},
|
||||
|
|
|
@ -24,7 +24,9 @@ let loadPrivatePackages;
|
|||
describe('Local Database', () => {
|
||||
beforeEach(() => {
|
||||
const writeMock = jest.spyOn(fs, 'writeFileSync').mockImplementation();
|
||||
loadPrivatePackages = jest.spyOn(pkgUtils, 'loadPrivatePackages').mockReturnValue({ list: [], secret: '' });
|
||||
loadPrivatePackages = jest
|
||||
.spyOn(pkgUtils, 'loadPrivatePackages')
|
||||
.mockReturnValue({ list: [], secret: '' });
|
||||
locaDatabase = new LocalDatabase(optionsPlugin.config, optionsPlugin.logger);
|
||||
(locaDatabase as LocalDatabase).clean();
|
||||
writeMock.mockClear();
|
||||
|
@ -77,7 +79,13 @@ describe('Local Database', () => {
|
|||
|
||||
if (storage) {
|
||||
const storagePath = path.normalize((storage as ILocalFSPackageManager).path).toLowerCase();
|
||||
expect(storagePath).toBe(path.normalize(path.join(__dirname, '__fixtures__', optionsPlugin.config.storage || '', pkgName)).toLowerCase());
|
||||
expect(storagePath).toBe(
|
||||
path
|
||||
.normalize(
|
||||
path.join(__dirname, '__fixtures__', optionsPlugin.config.storage || '', pkgName)
|
||||
)
|
||||
.toLowerCase()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -90,7 +98,17 @@ describe('Local Database', () => {
|
|||
if (storage) {
|
||||
const storagePath = path.normalize((storage as ILocalFSPackageManager).path).toLowerCase();
|
||||
expect(storagePath).toBe(
|
||||
path.normalize(path.join(__dirname, '__fixtures__', optionsPlugin.config.storage || '', 'private_folder', pkgName)).toLowerCase()
|
||||
path
|
||||
.normalize(
|
||||
path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
optionsPlugin.config.storage || '',
|
||||
'private_folder',
|
||||
pkgName
|
||||
)
|
||||
)
|
||||
.toLowerCase()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -153,7 +171,11 @@ describe('Local Database', () => {
|
|||
const scopedPackages = ['@pkg1/test'];
|
||||
const stats = { mtime: new Date() };
|
||||
jest.spyOn(fs, 'stat').mockImplementation((_, cb) => cb(null, stats as fs.Stats));
|
||||
jest.spyOn(fs, 'readdir').mockImplementation((storePath, cb) => cb(null, storePath.match('test-storage') ? scopedPackages : []));
|
||||
jest
|
||||
.spyOn(fs, 'readdir')
|
||||
.mockImplementation((storePath, cb) =>
|
||||
cb(null, storePath.match('test-storage') ? scopedPackages : [])
|
||||
);
|
||||
|
||||
callSearch(locaDatabase, 1, done);
|
||||
});
|
||||
|
@ -162,7 +184,11 @@ describe('Local Database', () => {
|
|||
const nonScopedPackages = ['pkg1', 'pkg2'];
|
||||
const stats = { mtime: new Date() };
|
||||
jest.spyOn(fs, 'stat').mockImplementation((_, cb) => cb(null, stats as fs.Stats));
|
||||
jest.spyOn(fs, 'readdir').mockImplementation((storePath, cb) => cb(null, storePath.match('test-storage') ? nonScopedPackages : []));
|
||||
jest
|
||||
.spyOn(fs, 'readdir')
|
||||
.mockImplementation((storePath, cb) =>
|
||||
cb(null, storePath.match('test-storage') ? nonScopedPackages : [])
|
||||
);
|
||||
|
||||
const db = new LocalDatabase(
|
||||
assign({}, optionsPlugin.config, {
|
||||
|
@ -176,7 +202,9 @@ describe('Local Database', () => {
|
|||
});
|
||||
|
||||
test('should fails on read the storage', (done) => {
|
||||
const spyInstance = jest.spyOn(fs, 'readdir').mockImplementation((_, cb) => cb(Error('fails'), null));
|
||||
const spyInstance = jest
|
||||
.spyOn(fs, 'readdir')
|
||||
.mockImplementation((_, cb) => cb(Error('fails'), null));
|
||||
|
||||
const db = new LocalDatabase(
|
||||
assign({}, optionsPlugin.config, {
|
||||
|
@ -267,7 +295,9 @@ describe('Local Database', () => {
|
|||
db.createReadStream.mockImplementation(() => stream);
|
||||
setTimeout(() => events.once['error'](new Error('Unexpected error!')));
|
||||
|
||||
await expect(locaDatabase.readTokens({ user: 'someUser' })).rejects.toThrow('Unexpected error!');
|
||||
await expect(locaDatabase.readTokens({ user: 'someUser' })).rejects.toThrow(
|
||||
'Unexpected error!'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -42,7 +42,10 @@ describe('Local FS test', () => {
|
|||
|
||||
describe('readPackage() group', () => {
|
||||
test('readPackage() success', (done) => {
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/readme-test'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/readme-test'),
|
||||
logger
|
||||
);
|
||||
|
||||
localFs.readPackage(pkgFileName, (err) => {
|
||||
expect(err).toBeNull();
|
||||
|
@ -51,7 +54,10 @@ describe('Local FS test', () => {
|
|||
});
|
||||
|
||||
test('readPackage() fails', (done) => {
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/readme-testt'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/readme-testt'),
|
||||
logger
|
||||
);
|
||||
|
||||
localFs.readPackage(pkgFileName, (err) => {
|
||||
expect(err).toBeTruthy();
|
||||
|
@ -60,7 +66,10 @@ describe('Local FS test', () => {
|
|||
});
|
||||
|
||||
test('readPackage() fails corrupt', (done) => {
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/readme-test-corrupt'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/readme-test-corrupt'),
|
||||
logger
|
||||
);
|
||||
|
||||
localFs.readPackage('corrupt.js', (err) => {
|
||||
expect(err).toBeTruthy();
|
||||
|
@ -108,7 +117,10 @@ describe('Local FS test', () => {
|
|||
});
|
||||
|
||||
test('removePackage() success', (done) => {
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(localTempStorage, '_toDelete'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(localTempStorage, '_toDelete'),
|
||||
logger
|
||||
);
|
||||
localFs.removePackage((error) => {
|
||||
expect(error).toBeNull();
|
||||
done();
|
||||
|
@ -116,7 +128,10 @@ describe('Local FS test', () => {
|
|||
});
|
||||
|
||||
test('removePackage() fails', (done) => {
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(localTempStorage, '_toDelete_fake'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(localTempStorage, '_toDelete_fake'),
|
||||
logger
|
||||
);
|
||||
localFs.removePackage((error) => {
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.code).toBe('ENOENT');
|
||||
|
@ -127,7 +142,10 @@ describe('Local FS test', () => {
|
|||
|
||||
describe('readTarball() group', () => {
|
||||
test('readTarball() success', (done) => {
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/readme-test'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/readme-test'),
|
||||
logger
|
||||
);
|
||||
const readTarballStream = localFs.readTarball('test-readme-0.0.0.tgz');
|
||||
|
||||
readTarballStream.on('error', function (err) {
|
||||
|
@ -148,7 +166,10 @@ describe('Local FS test', () => {
|
|||
});
|
||||
|
||||
test('readTarball() fails', (done) => {
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/readme-test'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/readme-test'),
|
||||
logger
|
||||
);
|
||||
const readTarballStream = localFs.readTarball('file-does-not-exist-0.0.0.tgz');
|
||||
|
||||
readTarballStream.on('error', function (err) {
|
||||
|
@ -167,8 +188,14 @@ describe('Local FS test', () => {
|
|||
|
||||
test('writeTarball() success', (done) => {
|
||||
const newFileName = 'new-readme-0.0.0.tgz';
|
||||
const readmeStorage: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/readme-test'), logger);
|
||||
const writeStorage: ILocalPackageManager = new LocalDriver(path.join(__dirname, '../_storage'), logger);
|
||||
const readmeStorage: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/readme-test'),
|
||||
logger
|
||||
);
|
||||
const writeStorage: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '../_storage'),
|
||||
logger
|
||||
);
|
||||
const readTarballStream = readmeStorage.readTarball('test-readme-0.0.0.tgz');
|
||||
const writeTarballStream = writeStorage.writeTarball(newFileName);
|
||||
|
||||
|
@ -207,7 +234,10 @@ describe('Local FS test', () => {
|
|||
test('writeTarball() abort', (done) => {
|
||||
const newFileLocationFolder: string = path.join(localTempStorage, '_writeTarball');
|
||||
const newFileName = 'new-readme-abort-0.0.0.tgz';
|
||||
const readmeStorage: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/readme-test'), logger);
|
||||
const readmeStorage: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/readme-test'),
|
||||
logger
|
||||
);
|
||||
const writeStorage: ILocalPackageManager = new LocalDriver(newFileLocationFolder, logger);
|
||||
const readTarballStream = readmeStorage.readTarball('test-readme-0.0.0.tgz');
|
||||
const writeTarballStream = writeStorage.writeTarball(newFileName);
|
||||
|
@ -249,7 +279,10 @@ describe('Local FS test', () => {
|
|||
});
|
||||
|
||||
const LocalDriver = require('../src/local-fs').default;
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/update-package'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/update-package'),
|
||||
logger
|
||||
);
|
||||
|
||||
localFs.updatePackage('updatePackage', updateHandler, onWrite, transform, () => {
|
||||
expect(transform).toHaveBeenCalledTimes(1);
|
||||
|
@ -268,7 +301,10 @@ describe('Local FS test', () => {
|
|||
};
|
||||
});
|
||||
require('../src/local-fs').default;
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/update-package'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/update-package'),
|
||||
logger
|
||||
);
|
||||
|
||||
localFs.updatePackage('updatePackage', updateHandler, onWrite, transform, (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
|
@ -287,7 +323,10 @@ describe('Local FS test', () => {
|
|||
};
|
||||
});
|
||||
const LocalDriver = require('../src/local-fs').default;
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/update-package'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/update-package'),
|
||||
logger
|
||||
);
|
||||
|
||||
localFs.updatePackage('updatePackage', updateHandler, onWrite, transform, (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
|
@ -306,7 +345,10 @@ describe('Local FS test', () => {
|
|||
};
|
||||
});
|
||||
const LocalDriver = require('../src/local-fs').default;
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/update-package'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/update-package'),
|
||||
logger
|
||||
);
|
||||
|
||||
localFs.updatePackage('updatePackage', updateHandler, onWrite, transform, (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
|
@ -326,7 +368,10 @@ describe('Local FS test', () => {
|
|||
});
|
||||
|
||||
const LocalDriver = require('../src/local-fs').default;
|
||||
const localFs: ILocalPackageManager = new LocalDriver(path.join(__dirname, '__fixtures__/update-package'), logger);
|
||||
const localFs: ILocalPackageManager = new LocalDriver(
|
||||
path.join(__dirname, '__fixtures__/update-package'),
|
||||
logger
|
||||
);
|
||||
const updateHandler = jest.fn((_name, cb) => {
|
||||
cb(fSError('something wrong', 500));
|
||||
});
|
||||
|
|
|
@ -42,25 +42,37 @@ describe('readme', () => {
|
|||
});
|
||||
|
||||
test('should parse basic / local storage', () => {
|
||||
expect(parseReadme('[Local Storage](javascript:alert(JSON.stringify(localStorage)))')).toEqual('<p><a>Local Storage</a></p>');
|
||||
expect(
|
||||
parseReadme('[Local Storage](javascript:alert(JSON.stringify(localStorage)))')
|
||||
).toEqual('<p><a>Local Storage</a></p>');
|
||||
});
|
||||
|
||||
test('should parse basic / case insensitive', () => {
|
||||
expect(parseReadme("[CaseInsensitive](JaVaScRiPt:alert('CaseInsensitive'))")).toEqual('<p><a>CaseInsensitive</a></p>');
|
||||
expect(parseReadme("[CaseInsensitive](JaVaScRiPt:alert('CaseInsensitive'))")).toEqual(
|
||||
'<p><a>CaseInsensitive</a></p>'
|
||||
);
|
||||
});
|
||||
|
||||
test('should parse basic / url', () => {
|
||||
expect(parseReadme("[URL](javascript://www.google.com%0Aalert('URL'))")).toEqual('<p><a>URL</a></p>');
|
||||
expect(parseReadme("[URL](javascript://www.google.com%0Aalert('URL'))")).toEqual(
|
||||
'<p><a>URL</a></p>'
|
||||
);
|
||||
});
|
||||
|
||||
test('should parse basic / in quotes', () => {
|
||||
expect(parseReadme('[In Quotes](\'javascript:alert("InQuotes")\')')).toEqual('<p><a href="\'javascript:alert(%22InQuotes%22)\'">In Quotes</a></p>');
|
||||
expect(parseReadme('[In Quotes](\'javascript:alert("InQuotes")\')')).toEqual(
|
||||
'<p><a href="\'javascript:alert(%22InQuotes%22)\'">In Quotes</a></p>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should parse images', () => {
|
||||
test('in quotes', () => {
|
||||
expect(parseReadme('![Escape SRC - onload](https://www.example.com/image.png"onload="alert(\'ImageOnLoad\'))')).toEqual(
|
||||
expect(
|
||||
parseReadme(
|
||||
'![Escape SRC - onload](https://www.example.com/image.png"onload="alert(\'ImageOnLoad\'))'
|
||||
)
|
||||
).toEqual(
|
||||
'<p><img alt="Escape SRC - onload" src="https://www.example.com/image.png%22onload=%22alert(\'ImageOnLoad\')"></p>'
|
||||
);
|
||||
});
|
||||
|
@ -78,17 +90,23 @@ describe('readme', () => {
|
|||
});
|
||||
|
||||
test('xss / white space cookie', () => {
|
||||
expect(parseReadme('[XSS](j a v a s c r i p t:prompt(document.cookie))')).toEqual(
|
||||
'<p>[XSS](j a v a s c r i p t:prompt(document.cookie))</p>'
|
||||
);
|
||||
expect(
|
||||
parseReadme('[XSS](j a v a s c r i p t:prompt(document.cookie))')
|
||||
).toEqual('<p>[XSS](j a v a s c r i p t:prompt(document.cookie))</p>');
|
||||
});
|
||||
|
||||
test('xss / data test/html', () => {
|
||||
expect(parseReadme('[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)')).toEqual('<p><a>XSS</a></p>');
|
||||
expect(
|
||||
parseReadme('[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)')
|
||||
).toEqual('<p><a>XSS</a></p>');
|
||||
});
|
||||
|
||||
test('xss / data test/html encoded', () => {
|
||||
expect(parseReadme('[XSS](javascript:alert('XSS'))')).toEqual(
|
||||
expect(
|
||||
parseReadme(
|
||||
'[XSS](javascript:alert('XSS'))'
|
||||
)
|
||||
).toEqual(
|
||||
'<p><a href="&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29">XSS</a></p>'
|
||||
);
|
||||
});
|
||||
|
@ -98,7 +116,9 @@ describe('readme', () => {
|
|||
});
|
||||
|
||||
test('xss / js window error alert', () => {
|
||||
expect(parseReadme('[XSS](javascript:window.onerror=alert;throw%20document.cookie)')).toEqual('<p><a>XSS</a></p>');
|
||||
expect(parseReadme('[XSS](javascript:window.onerror=alert;throw%20document.cookie)')).toEqual(
|
||||
'<p><a>XSS</a></p>'
|
||||
);
|
||||
});
|
||||
|
||||
test('xss / js window encoded prompt', () => {
|
||||
|
@ -110,15 +130,21 @@ describe('readme', () => {
|
|||
});
|
||||
|
||||
test('xss / js window encoded window error alert multiple statement', () => {
|
||||
expect(parseReadme('[XSS](javascript:window.onerror=alert;throw%20document.cookie)')).toEqual('<p><a>XSS</a></p>');
|
||||
expect(parseReadme('[XSS](javascript:window.onerror=alert;throw%20document.cookie)')).toEqual(
|
||||
'<p><a>XSS</a></p>'
|
||||
);
|
||||
});
|
||||
|
||||
test('xss / js window encoded window error alert throw error', () => {
|
||||
expect(parseReadme('[XSS](javascript://%0d%0awindow.onerror=alert;throw%20document.cookie)')).toEqual('<p><a>XSS</a></p>');
|
||||
expect(
|
||||
parseReadme('[XSS](javascript://%0d%0awindow.onerror=alert;throw%20document.cookie)')
|
||||
).toEqual('<p><a>XSS</a></p>');
|
||||
});
|
||||
|
||||
test('xss / js window encoded data text/html base 64', () => {
|
||||
expect(parseReadme('[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)')).toEqual('<p><a>XSS</a></p>');
|
||||
expect(
|
||||
parseReadme('[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)')
|
||||
).toEqual('<p><a>XSS</a></p>');
|
||||
});
|
||||
|
||||
test('xss / js vbscript alert', () => {
|
||||
|
@ -143,7 +169,9 @@ describe('readme', () => {
|
|||
});
|
||||
|
||||
test('xss / js case #5', () => {
|
||||
expect(parseReadme('[XSS](Javas%26%2399;ript:alert(1))')).toEqual('<p><a href="Javas%26%2399;ript:alert(1)">XSS</a></p>');
|
||||
expect(parseReadme('[XSS](Javas%26%2399;ript:alert(1))')).toEqual(
|
||||
'<p><a href="Javas%26%2399;ript:alert(1)">XSS</a></p>'
|
||||
);
|
||||
});
|
||||
|
||||
test('xss / js case #6', () => {
|
||||
|
@ -157,34 +185,48 @@ describe('readme', () => {
|
|||
|
||||
describe('xss / js url', () => {
|
||||
test('xss / case #1', () => {
|
||||
expect(parseReadme('[XSS](javascript://www.google.com%0Aprompt(1))')).toEqual('<p><a>XSS</a></p>');
|
||||
expect(parseReadme('[XSS](javascript://www.google.com%0Aprompt(1))')).toEqual(
|
||||
'<p><a>XSS</a></p>'
|
||||
);
|
||||
});
|
||||
|
||||
test('xss / case #2', () => {
|
||||
expect(parseReadme('[XSS](javascript://%0d%0aconfirm(1);com)')).toEqual('<p><a>XSS</a></p>');
|
||||
expect(parseReadme('[XSS](javascript://%0d%0aconfirm(1);com)')).toEqual(
|
||||
'<p><a>XSS</a></p>'
|
||||
);
|
||||
});
|
||||
|
||||
test('xss / case #3', () => {
|
||||
expect(parseReadme('[XSS](javascript:window.onerror=confirm;throw%201)')).toEqual('<p><a>XSS</a></p>');
|
||||
expect(parseReadme('[XSS](javascript:window.onerror=confirm;throw%201)')).toEqual(
|
||||
'<p><a>XSS</a></p>'
|
||||
);
|
||||
});
|
||||
|
||||
test('xss / case #4', () => {
|
||||
expect(parseReadme('[XSS](<28>javascript:alert(document.domain))')).toEqual('<p><a href="%EF%BF%BDjavascript:alert(document.domain)">XSS</a></p>');
|
||||
expect(parseReadme('[XSS](<28>javascript:alert(document.domain))')).toEqual(
|
||||
'<p><a href="%EF%BF%BDjavascript:alert(document.domain)">XSS</a></p>'
|
||||
);
|
||||
});
|
||||
|
||||
test('xss / case #5', () => {
|
||||
expect(parseReadme('![XSS](javascript:prompt(document.cookie))\\')).toEqual('<p><img alt="XSS">\\</p>');
|
||||
expect(parseReadme('![XSS](javascript:prompt(document.cookie))\\')).toEqual(
|
||||
'<p><img alt="XSS">\\</p>'
|
||||
);
|
||||
});
|
||||
|
||||
test('xss / case #6', () => {
|
||||
expect(parseReadme('![XSS](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)\\')).toEqual(
|
||||
expect(
|
||||
parseReadme('![XSS](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)\\')
|
||||
).toEqual(
|
||||
'<p><img alt="XSS" src="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">\\</p>'
|
||||
);
|
||||
});
|
||||
|
||||
// FIXME: requires proper parsing
|
||||
test.skip('xss / case #7', () => {
|
||||
expect(parseReadme(`![XSS'"\`onerror=prompt(document.cookie)](x)\\`)).toEqual('<p>![XSS\'\\"`onerror=prompt(document.cookie)](x)\\\\</p>');
|
||||
expect(parseReadme(`![XSS'"\`onerror=prompt(document.cookie)](x)\\`)).toEqual(
|
||||
'<p>![XSS\'\\"`onerror=prompt(document.cookie)](x)\\\\</p>'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
26
packages/core/types/index.d.ts
vendored
26
packages/core/types/index.d.ts
vendored
|
@ -410,7 +410,11 @@ declare module '@verdaccio/types' {
|
|||
getSecret(): Promise<string>;
|
||||
setSecret(secret: string): Promise<any>;
|
||||
getPackageStorage(packageInfo: string): IPackageStorage;
|
||||
search(onPackage: onSearchPackage, onEnd: onEndSearchPackage, validateName: onValidatePackage): void;
|
||||
search(
|
||||
onPackage: onSearchPackage,
|
||||
onEnd: onEndSearchPackage,
|
||||
validateName: onValidatePackage
|
||||
): void;
|
||||
}
|
||||
|
||||
type StorageUpdateCallback = (data: Package, cb: CallbackAction) => void;
|
||||
|
@ -444,7 +448,13 @@ declare module '@verdaccio/types' {
|
|||
}
|
||||
|
||||
interface StoragePackageActions extends TarballActions {
|
||||
addVersion(name: string, version: string, metadata: Version, tag: StringValue, callback: Callback): void;
|
||||
addVersion(
|
||||
name: string,
|
||||
version: string,
|
||||
metadata: Version,
|
||||
tag: StringValue,
|
||||
callback: Callback
|
||||
): void;
|
||||
mergeTags(name: string, tags: MergeTags, callback: Callback): void;
|
||||
removePackage(name: string, callback: Callback): void;
|
||||
changePackage(name: string, metadata: Package, revision: string, callback: Callback): void;
|
||||
|
@ -521,9 +531,17 @@ declare module '@verdaccio/types' {
|
|||
allow_publish?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void;
|
||||
allow_access?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void;
|
||||
allow_unpublish?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void;
|
||||
allow_publish?(user: RemoteUser, pkg: AllowAccess & PackageAccess, cb: AuthAccessCallback): void;
|
||||
allow_publish?(
|
||||
user: RemoteUser,
|
||||
pkg: AllowAccess & PackageAccess,
|
||||
cb: AuthAccessCallback
|
||||
): void;
|
||||
allow_access?(user: RemoteUser, pkg: AllowAccess & PackageAccess, cb: AuthAccessCallback): void;
|
||||
allow_unpublish?(user: RemoteUser, pkg: AllowAccess & PackageAccess, cb: AuthAccessCallback): void;
|
||||
allow_unpublish?(
|
||||
user: RemoteUser,
|
||||
pkg: AllowAccess & PackageAccess,
|
||||
cb: AuthAccessCallback
|
||||
): void;
|
||||
apiJWTmiddleware?(helpers: any): Function;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
"@verdaccio/auth": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/config": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/dev-commons": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/dev-types": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/types": "workspace:*",
|
||||
"@verdaccio/utils": "workspace:5.0.0-alpha.0"
|
||||
},
|
||||
|
|
|
@ -7,7 +7,12 @@ import { notifyRequest } from './notify-request';
|
|||
|
||||
type TemplateMetadata = Package & { publishedPackage: string };
|
||||
|
||||
export function handleNotify(metadata: Package, notifyEntry, remoteUser: RemoteUser, publishedPackage: string): Promise<any> | void {
|
||||
export function handleNotify(
|
||||
metadata: Package,
|
||||
notifyEntry,
|
||||
remoteUser: RemoteUser,
|
||||
publishedPackage: string
|
||||
): Promise<any> | void {
|
||||
let regex;
|
||||
if (metadata.name && notifyEntry.packagePattern) {
|
||||
regex = new RegExp(notifyEntry.packagePattern, notifyEntry.packagePatternFlags || '');
|
||||
|
@ -60,17 +65,34 @@ export function handleNotify(metadata: Package, notifyEntry, remoteUser: RemoteU
|
|||
return notifyRequest(options, content);
|
||||
}
|
||||
|
||||
export function sendNotification(metadata: Package, notify: Notification, remoteUser: RemoteUser, publishedPackage: string): Promise<any> {
|
||||
export function sendNotification(
|
||||
metadata: Package,
|
||||
notify: Notification,
|
||||
remoteUser: RemoteUser,
|
||||
publishedPackage: string
|
||||
): Promise<any> {
|
||||
return handleNotify(metadata, notify, remoteUser, publishedPackage) as Promise<any>;
|
||||
}
|
||||
|
||||
export function notify(metadata: Package, config: Config, remoteUser: RemoteUser, publishedPackage: string): Promise<any> | void {
|
||||
export function notify(
|
||||
metadata: Package,
|
||||
config: Config,
|
||||
remoteUser: RemoteUser,
|
||||
publishedPackage: string
|
||||
): Promise<any> | void {
|
||||
if (config.notify) {
|
||||
if (config.notify.content) {
|
||||
return sendNotification(metadata, (config.notify as unknown) as Notification, remoteUser, publishedPackage);
|
||||
return sendNotification(
|
||||
metadata,
|
||||
(config.notify as unknown) as Notification,
|
||||
remoteUser,
|
||||
publishedPackage
|
||||
);
|
||||
}
|
||||
// multiple notifications endpoints PR #108
|
||||
return Promise.all(_.map(config.notify, (key) => sendNotification(metadata, key, remoteUser, publishedPackage)));
|
||||
return Promise.all(
|
||||
_.map(config.notify, (key) => sendNotification(metadata, key, remoteUser, publishedPackage))
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
|
|
|
@ -15,8 +15,12 @@ const parseConfigurationNotifyFile = (name) => {
|
|||
return parseConfigurationFile(`notify/${name}`);
|
||||
};
|
||||
const singleNotificationConfig = parseConfigFile(parseConfigurationNotifyFile('single.notify'));
|
||||
const singleHeaderNotificationConfig = parseConfigFile(parseConfigurationNotifyFile('single.header.notify'));
|
||||
const packagePatternNotificationConfig = parseConfigFile(parseConfigurationNotifyFile('single.packagePattern.notify'));
|
||||
const singleHeaderNotificationConfig = parseConfigFile(
|
||||
parseConfigurationNotifyFile('single.header.notify')
|
||||
);
|
||||
const packagePatternNotificationConfig = parseConfigFile(
|
||||
parseConfigurationNotifyFile('single.packagePattern.notify')
|
||||
);
|
||||
const multiNotificationConfig = parseConfigFile(parseConfigurationNotifyFile('multiple.notify'));
|
||||
|
||||
describe('Notifications:: Notify', () => {
|
||||
|
|
|
@ -38,7 +38,10 @@ describe('Notifications:: notifyRequest', () => {
|
|||
});
|
||||
|
||||
const notification = require('../src/notify-request');
|
||||
const args = [{ errorMessage: 'bad data' }, 'notify service has thrown an error: @{errorMessage}'];
|
||||
const args = [
|
||||
{ errorMessage: 'bad data' },
|
||||
'notify service has thrown an error: @{errorMessage}',
|
||||
];
|
||||
|
||||
await expect(notification.notifyRequest(options, content)).rejects.toEqual(API_ERROR.BAD_DATA);
|
||||
expect(logger.logger.error).toHaveBeenCalledWith(...args);
|
||||
|
@ -55,7 +58,10 @@ describe('Notifications:: notifyRequest', () => {
|
|||
});
|
||||
|
||||
const notification = require('../src/notify-request');
|
||||
const args = [{ errorMessage: 'bad data' }, 'notify service has thrown an error: @{errorMessage}'];
|
||||
const args = [
|
||||
{ errorMessage: 'bad data' },
|
||||
'notify service has thrown an error: @{errorMessage}',
|
||||
];
|
||||
|
||||
await expect(notification.notifyRequest(options, content)).rejects.toEqual(API_ERROR.BAD_DATA);
|
||||
expect(logger.logger.error).toHaveBeenCalledWith(...args);
|
||||
|
@ -75,7 +81,9 @@ describe('Notifications:: notifyRequest', () => {
|
|||
const infoArgs = [{ content }, 'A notification has been shipped: @{content}'];
|
||||
const debugArgs = [{ body: 'Successfully delivered' }, ' body: @{body}'];
|
||||
|
||||
await expect(notification.notifyRequest(options, content)).resolves.toEqual('Successfully delivered');
|
||||
await expect(notification.notifyRequest(options, content)).resolves.toEqual(
|
||||
'Successfully delivered'
|
||||
);
|
||||
expect(logger.logger.info).toHaveBeenCalledWith(...infoArgs);
|
||||
expect(logger.logger.debug).toHaveBeenCalledWith(...debugArgs);
|
||||
});
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/mock": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/dev-types": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/config": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/commons-api": "workspace:*",
|
||||
"@verdaccio/types": "workspace:*"
|
||||
|
|
|
@ -50,7 +50,13 @@ function isES6(plugin): boolean {
|
|||
* @param {*} sanityCheck callback that check the shape that should fulfill the plugin
|
||||
* @return {Array} list of plugins
|
||||
*/
|
||||
export function loadPlugin<T extends IPlugin<T>>(config: Config, pluginConfigs: any = {}, params: any, sanityCheck: any, prefix: string = 'verdaccio'): any[] {
|
||||
export function loadPlugin<T extends IPlugin<T>>(
|
||||
config: Config,
|
||||
pluginConfigs: any = {},
|
||||
params: any,
|
||||
sanityCheck: any,
|
||||
prefix: string = 'verdaccio'
|
||||
): any[] {
|
||||
return Object.keys(pluginConfigs).map(
|
||||
(pluginId: string): IPlugin<T> => {
|
||||
let plugin;
|
||||
|
@ -94,19 +100,27 @@ export function loadPlugin<T extends IPlugin<T>>(config: Config, pluginConfigs:
|
|||
}
|
||||
|
||||
if (plugin === null) {
|
||||
logger.error({ content: pluginId, prefix }, 'plugin not found. try npm install @{prefix}-@{content}');
|
||||
logger.error(
|
||||
{ content: pluginId, prefix },
|
||||
'plugin not found. try npm install @{prefix}-@{content}'
|
||||
);
|
||||
throw Error(`
|
||||
${prefix}-${pluginId} plugin not found. try "npm install ${prefix}-${pluginId}"`);
|
||||
}
|
||||
|
||||
if (!isValid(plugin)) {
|
||||
logger.error({ content: pluginId }, '@{prefix}-@{content} plugin does not have the right code structure');
|
||||
logger.error(
|
||||
{ content: pluginId },
|
||||
'@{prefix}-@{content} plugin does not have the right code structure'
|
||||
);
|
||||
throw Error(`"${pluginId}" plugin does not have the right code structure`);
|
||||
}
|
||||
|
||||
/* eslint new-cap:off */
|
||||
try {
|
||||
plugin = isES6(plugin) ? new plugin.default(mergeConfig(config, pluginConfigs[pluginId]), params) : plugin(pluginConfigs[pluginId], params);
|
||||
plugin = isES6(plugin)
|
||||
? new plugin.default(mergeConfig(config, pluginConfigs[pluginId]), params)
|
||||
: plugin(pluginConfigs[pluginId], params);
|
||||
} catch (error) {
|
||||
plugin = null;
|
||||
logger.error({ error, pluginId }, 'error loading a plugin @{pluginId}: @{error}');
|
||||
|
@ -114,7 +128,10 @@ export function loadPlugin<T extends IPlugin<T>>(config: Config, pluginConfigs:
|
|||
/* eslint new-cap:off */
|
||||
|
||||
if (plugin === null || !sanityCheck(plugin)) {
|
||||
logger.error({ content: pluginId, prefix }, "@{prefix}-@{content} doesn't look like a valid plugin");
|
||||
logger.error(
|
||||
{ content: pluginId, prefix },
|
||||
"@{prefix}-@{content} doesn't look like a valid plugin"
|
||||
);
|
||||
throw Error(`sanity check has failed, "${pluginId}" is not a valid plugin`);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,9 @@ describe('plugin loader', () => {
|
|||
return p.authenticate || p.allow_access || p.allow_publish;
|
||||
});
|
||||
} catch (e) {
|
||||
expect(e.message).toEqual(`"${relativePath}/invalid-plugin" plugin does not have the right code structure`);
|
||||
expect(e.message).toEqual(
|
||||
`"${relativePath}/invalid-plugin" plugin does not have the right code structure`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -58,7 +60,9 @@ describe('plugin loader', () => {
|
|||
return plugin.authenticate || plugin.allow_access || plugin.allow_publish;
|
||||
});
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual(`sanity check has failed, "${relativePath}/invalid-plugin-sanity" is not a valid plugin`);
|
||||
expect(err.message).toEqual(
|
||||
`sanity check has failed, "${relativePath}/invalid-plugin-sanity" is not a valid plugin`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -71,7 +75,9 @@ describe('plugin loader', () => {
|
|||
});
|
||||
} catch (e) {
|
||||
expect(e.message).toMatch('plugin not found');
|
||||
expect(e.message.replace(/\\/g, '/')).toMatch('/partials/test-plugin-storage/invalid-package');
|
||||
expect(e.message.replace(/\\/g, '/')).toMatch(
|
||||
'/partials/test-plugin-storage/invalid-package'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -62,7 +62,9 @@ function getMessage(debugLevel, msg, sub, templateObjects, hasColors) {
|
|||
|
||||
const subSystemType = subSystemLevels.color[sub ?? 'default'];
|
||||
if (hasColors) {
|
||||
const logString = `${levelsColors[debugLevel](pad(debugLevel, LEVEL_VALUE_MAX))}${white(`${subSystemType} ${finalMessage}`)}`;
|
||||
const logString = `${levelsColors[debugLevel](pad(debugLevel, LEVEL_VALUE_MAX))}${white(
|
||||
`${subSystemType} ${finalMessage}`
|
||||
)}`;
|
||||
|
||||
return padLeft(logString, logString.length + CUSTOM_PAD_LENGTH, ' ');
|
||||
}
|
||||
|
@ -71,7 +73,11 @@ function getMessage(debugLevel, msg, sub, templateObjects, hasColors) {
|
|||
return padLeft(logString, logString.length + CUSTOM_PAD_LENGTH, ' ');
|
||||
}
|
||||
|
||||
export function printMessage(templateObjects: ObjectTemplate, options: PrettyOptionsExtended, hasColors = true): string {
|
||||
export function printMessage(
|
||||
templateObjects: ObjectTemplate,
|
||||
options: PrettyOptionsExtended,
|
||||
hasColors = true
|
||||
): string {
|
||||
const { prettyStamp } = options;
|
||||
const { level, msg, sub } = templateObjects;
|
||||
const debugLevel = calculateLevel(level);
|
||||
|
|
|
@ -67,7 +67,8 @@ describe('formatter', () => {
|
|||
status: 200,
|
||||
error: undefined,
|
||||
bytes: { in: 0, out: 150186 },
|
||||
msg: "@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}', bytes: @{bytes.in}/@{bytes.out}",
|
||||
msg:
|
||||
"@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}', bytes: @{bytes.in}/@{bytes.out}",
|
||||
};
|
||||
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
|
@ -84,7 +85,9 @@ describe('formatter', () => {
|
|||
err: {
|
||||
type: 'Error',
|
||||
message: 'getaddrinfo ENOTFOUND registry.fake.org',
|
||||
stack: 'Error: getaddrinfo ENOTFOUND registry.fake.org\n' + ' at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:60:26)',
|
||||
stack:
|
||||
'Error: getaddrinfo ENOTFOUND registry.fake.org\n' +
|
||||
' at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:60:26)',
|
||||
errno: -3008,
|
||||
code: 'ENOTFOUND',
|
||||
syscall: 'getaddrinfo',
|
||||
|
|
|
@ -18,7 +18,12 @@ export type LogPlugin = {
|
|||
export type LogType = 'file' | 'stdout';
|
||||
export type LogFormat = 'json' | 'pretty-timestamped' | 'pretty';
|
||||
|
||||
export function createLogger(options = {}, destination = pino.destination(1), format: LogFormat = DEFAULT_LOG_FORMAT, prettyPrintOptions = {}) {
|
||||
export function createLogger(
|
||||
options = {},
|
||||
destination = pino.destination(1),
|
||||
format: LogFormat = DEFAULT_LOG_FORMAT,
|
||||
prettyPrintOptions = {}
|
||||
) {
|
||||
if (_.isNil(format)) {
|
||||
format = DEFAULT_LOG_FORMAT;
|
||||
}
|
||||
|
|
|
@ -26,10 +26,7 @@
|
|||
"@verdaccio/dev-commons": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/logger": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/utils": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/auth": "workspace:5.0.0-alpha.0",
|
||||
"lodash": "4.17.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/dev-types": "workspace:5.0.0-alpha.0"
|
||||
},
|
||||
"gitHead": "7c246ede52ff717707fcae66dd63fc4abd536982"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,15 +8,34 @@ import {
|
|||
stringToMD5,
|
||||
ErrorCode,
|
||||
} from '@verdaccio/utils';
|
||||
import { API_ERROR, HEADER_TYPE, HEADERS, HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
import { $ResponseExtend, $RequestExtend, $NextFunctionVer, IAuth } from '@verdaccio/dev-types';
|
||||
import { Config, Package, RemoteUser } from '@verdaccio/types';
|
||||
import {
|
||||
API_ERROR,
|
||||
HEADER_TYPE,
|
||||
HEADERS,
|
||||
HTTP_STATUS,
|
||||
TOKEN_BASIC,
|
||||
TOKEN_BEARER,
|
||||
} from '@verdaccio/dev-commons';
|
||||
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
|
||||
import { Config, Package, RemoteUser, Logger } from '@verdaccio/types';
|
||||
import { logger } from '@verdaccio/logger';
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
import { HttpError } from 'http-errors';
|
||||
|
||||
export type $RequestExtend = Request & { remote_user?: any; log: Logger };
|
||||
export type $ResponseExtend = Response & { cookies?: any };
|
||||
export type $NextFunctionVer = NextFunction & any;
|
||||
|
||||
export function match(regexp: RegExp): any {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer, value: string): void {
|
||||
return function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer,
|
||||
value: string
|
||||
): void {
|
||||
if (regexp.exec(value)) {
|
||||
next();
|
||||
} else {
|
||||
|
@ -25,7 +44,11 @@ export function match(regexp: RegExp): any {
|
|||
};
|
||||
}
|
||||
|
||||
export function setSecurityWebHeaders(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
export function setSecurityWebHeaders(
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
// disable loading in frames (clickjacking, etc.)
|
||||
res.header(HEADERS.FRAMES_OPTIONS, 'deny');
|
||||
// avoid stablish connections outside of domain
|
||||
|
@ -39,7 +62,13 @@ export function setSecurityWebHeaders(req: $RequestExtend, res: $ResponseExtend,
|
|||
|
||||
// flow: express does not match properly
|
||||
// flow info https://github.com/flowtype/flow-typed/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+express
|
||||
export function validateName(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer, value: string, name: string): void {
|
||||
export function validateName(
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer,
|
||||
value: string,
|
||||
name: string
|
||||
): void {
|
||||
if (value === '-') {
|
||||
// special case in couchdb usually
|
||||
next('route');
|
||||
|
@ -52,7 +81,13 @@ export function validateName(req: $RequestExtend, res: $ResponseExtend, next: $N
|
|||
|
||||
// flow: express does not match properly
|
||||
// flow info https://github.com/flowtype/flow-typed/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+express
|
||||
export function validatePackage(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer, value: string, name: string): void {
|
||||
export function validatePackage(
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer,
|
||||
value: string,
|
||||
name: string
|
||||
): void {
|
||||
if (value === '-') {
|
||||
// special case in couchdb usually
|
||||
next('route');
|
||||
|
@ -66,14 +101,26 @@ export function validatePackage(req: $RequestExtend, res: $ResponseExtend, next:
|
|||
export function media(expect: string | null): any {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
if (req.headers[HEADER_TYPE.CONTENT_TYPE] !== expect) {
|
||||
next(ErrorCode.getCode(HTTP_STATUS.UNSUPPORTED_MEDIA, 'wrong content-type, expect: ' + expect + ', got: ' + req.headers[HEADER_TYPE.CONTENT_TYPE]));
|
||||
next(
|
||||
ErrorCode.getCode(
|
||||
HTTP_STATUS.UNSUPPORTED_MEDIA,
|
||||
'wrong content-type, expect: ' +
|
||||
expect +
|
||||
', got: ' +
|
||||
req.headers[HEADER_TYPE.CONTENT_TYPE]
|
||||
)
|
||||
);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function encodeScopePackage(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
export function encodeScopePackage(
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
if (req.url.indexOf('@') !== -1) {
|
||||
// e.g.: /@org/pkg/1.2.3 -> /@org%2Fpkg/1.2.3, /@org%2Fpkg/1.2.3 -> /@org%2Fpkg/1.2.3
|
||||
req.url = req.url.replace(/^(\/@[^\/%]+)\/(?!$)/, '$1%2F');
|
||||
|
@ -81,7 +128,11 @@ export function encodeScopePackage(req: $RequestExtend, res: $ResponseExtend, ne
|
|||
next();
|
||||
}
|
||||
|
||||
export function expectJson(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
export function expectJson(
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
if (!isObject(req.body)) {
|
||||
return next(ErrorCode.getBadRequest("can't parse incoming json"));
|
||||
}
|
||||
|
@ -108,11 +159,21 @@ export function allow(auth: IAuth): Function {
|
|||
return function (action: string): Function {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
req.pause();
|
||||
const packageName = req.params.scope ? `@${req.params.scope}/${req.params.package}` : req.params.package;
|
||||
const packageVersion = req.params.filename ? getVersionFromTarball(req.params.filename) : undefined;
|
||||
const packageName = req.params.scope
|
||||
? `@${req.params.scope}/${req.params.package}`
|
||||
: req.params.package;
|
||||
const packageVersion = req.params.filename
|
||||
? getVersionFromTarball(req.params.filename)
|
||||
: undefined;
|
||||
const remote: RemoteUser = req.remote_user;
|
||||
logger.trace({ action, user: remote?.name }, `[middleware/allow][@{action}] allow for @{user}`);
|
||||
auth['allow_' + action]({ packageName, packageVersion }, remote, function (error, allowed): void {
|
||||
logger.trace(
|
||||
{ action, user: remote?.name },
|
||||
`[middleware/allow][@{action}] allow for @{user}`
|
||||
);
|
||||
auth['allow_' + action]({ packageName, packageVersion }, remote, function (
|
||||
error,
|
||||
allowed
|
||||
): void {
|
||||
req.resume();
|
||||
if (error) {
|
||||
next(error);
|
||||
|
@ -134,7 +195,12 @@ export interface MiddlewareError {
|
|||
|
||||
export type FinalBody = Package | MiddlewareError | string;
|
||||
|
||||
export function final(body: FinalBody, req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
export function final(
|
||||
body: FinalBody,
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
if (res.statusCode === HTTP_STATUS.UNAUTHORIZED && !res.getHeader(HEADERS.WWW_AUTH)) {
|
||||
// they say it's required for 401, so...
|
||||
res.header(HEADERS.WWW_AUTH, `${TOKEN_BASIC}, ${TOKEN_BEARER}`);
|
||||
|
@ -155,7 +221,10 @@ export function final(body: FinalBody, req: $RequestExtend, res: $ResponseExtend
|
|||
}
|
||||
|
||||
// don't send etags with errors
|
||||
if (!res.statusCode || (res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)) {
|
||||
if (
|
||||
!res.statusCode ||
|
||||
(res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)
|
||||
) {
|
||||
res.header(HEADERS.ETAG, '"' + stringToMD5(body as string) + '"');
|
||||
}
|
||||
} else {
|
||||
|
@ -178,7 +247,8 @@ export function final(body: FinalBody, req: $RequestExtend, res: $ResponseExtend
|
|||
}
|
||||
|
||||
// FIXME: deprecated, moved to @verdaccio/dev-commons
|
||||
export const LOG_STATUS_MESSAGE = "@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}'";
|
||||
export const LOG_STATUS_MESSAGE =
|
||||
"@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}'";
|
||||
export const LOG_VERDACCIO_ERROR = `${LOG_STATUS_MESSAGE}, error: @{!error}`;
|
||||
export const LOG_VERDACCIO_BYTES = `${LOG_STATUS_MESSAGE}, bytes: @{bytes.in}/@{bytes.out}`;
|
||||
|
||||
|
@ -274,7 +344,12 @@ export function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFuncti
|
|||
next();
|
||||
}
|
||||
|
||||
export function handleError(err: HttpError, req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
export function handleError(
|
||||
err: HttpError,
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
) {
|
||||
if (_.isError(err)) {
|
||||
if (err.code === 'ECONNABORT' && res.statusCode === HTTP_STATUS.NOT_MODIFIED) {
|
||||
return next();
|
||||
|
@ -292,7 +367,11 @@ export function handleError(err: HttpError, req: $RequestExtend, res: $ResponseE
|
|||
}
|
||||
|
||||
// Middleware
|
||||
export function errorReportingMiddleware(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
export function errorReportingMiddleware(
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
res.locals.report_error =
|
||||
res.locals.report_error ||
|
||||
function (err: VerdaccioError): void {
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
"verdaccio": "^4.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/dev-types": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/types": "workspace:*"
|
||||
},
|
||||
"gitHead": "7c246ede52ff717707fcae66dd63fc4abd536982"
|
||||
|
|
|
@ -7,7 +7,9 @@ import { parseConfigFile } from '@verdaccio/utils';
|
|||
* Override the default.yaml configuration file with any new config provided.
|
||||
*/
|
||||
function configExample(externalConfig, configFile: string = 'default.yaml', location: string = '') {
|
||||
const locationFile = location ? path.join(location, configFile) : path.join(__dirname, `./config/yaml/${configFile}`);
|
||||
const locationFile = location
|
||||
? path.join(location, configFile)
|
||||
: path.join(__dirname, `./config/yaml/${configFile}`);
|
||||
const config = parseConfigFile(locationFile);
|
||||
|
||||
return _.assign({}, _.cloneDeep(config), externalConfig);
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import _ from 'lodash';
|
||||
import request from 'supertest';
|
||||
|
||||
import { DIST_TAGS, LATEST, HEADER_TYPE, HEADERS, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
import {
|
||||
DIST_TAGS,
|
||||
LATEST,
|
||||
HEADER_TYPE,
|
||||
HEADERS,
|
||||
HTTP_STATUS,
|
||||
TOKEN_BEARER,
|
||||
} from '@verdaccio/dev-commons';
|
||||
import { buildToken, encodeScopedUri } from '@verdaccio/utils';
|
||||
import { generateRandomHexString } from '@verdaccio/utils';
|
||||
import { Package } from '@verdaccio/types';
|
||||
|
@ -29,9 +36,17 @@ export function getTaggedVersionFromPackage(pkg, pkgName, tag: string = LATEST,
|
|||
return latestPkg;
|
||||
}
|
||||
|
||||
export function putPackage(request: any, pkgName: string, publishMetadata: Package, token?: string): Promise<any[]> {
|
||||
export function putPackage(
|
||||
request: any,
|
||||
pkgName: string,
|
||||
publishMetadata: Package,
|
||||
token?: string
|
||||
): Promise<any[]> {
|
||||
return new Promise((resolve) => {
|
||||
const put = request.put(pkgName).set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON).send(JSON.stringify(publishMetadata));
|
||||
const put = request
|
||||
.put(pkgName)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(publishMetadata));
|
||||
|
||||
if (_.isEmpty(token) === false) {
|
||||
expect(token).toBeDefined();
|
||||
|
@ -50,7 +65,9 @@ export function putPackage(request: any, pkgName: string, publishMetadata: Packa
|
|||
|
||||
export function deletePackage(request: any, pkgName: string, token?: string): Promise<any[]> {
|
||||
return new Promise((resolve) => {
|
||||
const del = request.put(`/${encodeScopedUri(pkgName)}/-rev/${generateRandomHexString(8)}`).set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
||||
const del = request
|
||||
.put(`/${encodeScopedUri(pkgName)}/-rev/${generateRandomHexString(8)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
||||
|
||||
if (_.isNil(token) === false) {
|
||||
del.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token as string));
|
||||
|
@ -65,7 +82,12 @@ export function deletePackage(request: any, pkgName: string, token?: string): Pr
|
|||
});
|
||||
}
|
||||
|
||||
export function getPackage(request: any, token: string, pkgName: string, statusCode: number = HTTP_STATUS.OK): Promise<any[]> {
|
||||
export function getPackage(
|
||||
request: any,
|
||||
token: string,
|
||||
pkgName: string,
|
||||
statusCode: number = HTTP_STATUS.OK
|
||||
): Promise<any[]> {
|
||||
return new Promise((resolve) => {
|
||||
const getRequest = request.get(`/${pkgName}`);
|
||||
|
||||
|
@ -82,7 +104,13 @@ export function getPackage(request: any, token: string, pkgName: string, statusC
|
|||
});
|
||||
}
|
||||
|
||||
export function loginUserToken(request: any, user: string, credentials: any, token: string, statusCode: number = HTTP_STATUS.CREATED): Promise<any[]> {
|
||||
export function loginUserToken(
|
||||
request: any,
|
||||
user: string,
|
||||
credentials: any,
|
||||
token: string,
|
||||
statusCode: number = HTTP_STATUS.CREATED
|
||||
): Promise<any[]> {
|
||||
// $FlowFixMe
|
||||
return new Promise((resolve) => {
|
||||
request
|
||||
|
@ -97,7 +125,12 @@ export function loginUserToken(request: any, user: string, credentials: any, tok
|
|||
});
|
||||
}
|
||||
|
||||
export function addUser(request: any, user: string, credentials: any, statusCode: number = HTTP_STATUS.CREATED): Promise<any[]> {
|
||||
export function addUser(
|
||||
request: any,
|
||||
user: string,
|
||||
credentials: any,
|
||||
statusCode: number = HTTP_STATUS.CREATED
|
||||
): Promise<any[]> {
|
||||
// $FlowFixMe
|
||||
return new Promise((resolve) => {
|
||||
request
|
||||
|
@ -124,7 +157,11 @@ export async function getNewToken(request: any, credentials: any): Promise<strin
|
|||
});
|
||||
}
|
||||
|
||||
export function getProfile(request: any, token: string, statusCode: number = HTTP_STATUS.OK): Promise<any[]> {
|
||||
export function getProfile(
|
||||
request: any,
|
||||
token: string,
|
||||
statusCode: number = HTTP_STATUS.OK
|
||||
): Promise<any[]> {
|
||||
// $FlowFixMe
|
||||
return new Promise((resolve) => {
|
||||
request
|
||||
|
@ -138,7 +175,12 @@ export function getProfile(request: any, token: string, statusCode: number = HTT
|
|||
});
|
||||
}
|
||||
|
||||
export function postProfile(request: any, body: any, token: string, statusCode: number = HTTP_STATUS.OK): Promise<any[]> {
|
||||
export function postProfile(
|
||||
request: any,
|
||||
body: any,
|
||||
token: string,
|
||||
statusCode: number = HTTP_STATUS.OK
|
||||
): Promise<any[]> {
|
||||
// $FlowFixMe
|
||||
return new Promise((resolve) => {
|
||||
request
|
||||
|
@ -153,7 +195,13 @@ export function postProfile(request: any, body: any, token: string, statusCode:
|
|||
});
|
||||
}
|
||||
|
||||
export async function fetchPackageByVersionAndTag(app, encodedPkgName, pkgName, version, tag = 'latest') {
|
||||
export async function fetchPackageByVersionAndTag(
|
||||
app,
|
||||
encodedPkgName,
|
||||
pkgName,
|
||||
version,
|
||||
tag = 'latest'
|
||||
) {
|
||||
// we retrieve the package to verify
|
||||
const [err, resp] = await getPackage(request(app), '', encodedPkgName);
|
||||
|
||||
|
@ -170,7 +218,12 @@ export async function isExistPackage(app, packageName) {
|
|||
}
|
||||
|
||||
export async function verifyPackageVersionDoesExist(app, packageName, version, token?: string) {
|
||||
const [, res] = await getPackage(request(app), token as string, encodeScopedUri(packageName), HTTP_STATUS.OK);
|
||||
const [, res] = await getPackage(
|
||||
request(app),
|
||||
token as string,
|
||||
encodeScopedUri(packageName),
|
||||
HTTP_STATUS.OK
|
||||
);
|
||||
|
||||
const { versions } = res.body;
|
||||
const versionsKeys = Object.keys(versions);
|
||||
|
|
|
@ -81,7 +81,12 @@ export function mockServer(port: number, options: MockRegistryOptions = {}) {
|
|||
fs.copyFileSync(finalOptions.configPath!, tempConfigFile);
|
||||
fsExtra.copySync(finalOptions.storePath!, storePath);
|
||||
|
||||
const verdaccioConfig = new VerdaccioConfig(storePath, tempConfigFile, `http://${DOMAIN_SERVERS}:${port}/`, port);
|
||||
const verdaccioConfig = new VerdaccioConfig(
|
||||
storePath,
|
||||
tempConfigFile,
|
||||
`http://${DOMAIN_SERVERS}:${port}/`,
|
||||
port
|
||||
);
|
||||
const server: IServerBridge = new Server(verdaccioConfig.domainPath);
|
||||
|
||||
return new VerdaccioProcess(verdaccioConfig, server, finalOptions.silence, finalOptions.debug);
|
||||
|
|
|
@ -147,7 +147,12 @@ export default class Server implements IServerBridge {
|
|||
}).send(JSON.stringify(version));
|
||||
}
|
||||
|
||||
public putTarballIncomplete(pkgName: string, filename: string, data: any, headerContentSize: number): Promise<any> {
|
||||
public putTarballIncomplete(
|
||||
pkgName: string,
|
||||
filename: string,
|
||||
data: any,
|
||||
headerContentSize: number
|
||||
): Promise<any> {
|
||||
const promise = this.request({
|
||||
uri: `/${encodeURIComponent(pkgName)}/-/${encodeURIComponent(filename)}/whatever`,
|
||||
method: 'PUT',
|
||||
|
@ -183,7 +188,9 @@ export default class Server implements IServerBridge {
|
|||
}
|
||||
|
||||
public addPackage(name: string) {
|
||||
return this.putPackage(name, getPackage(name)).status(HTTP_STATUS.CREATED).body_ok(API_MESSAGE.PKG_CREATED);
|
||||
return this.putPackage(name, getPackage(name))
|
||||
.status(HTTP_STATUS.CREATED)
|
||||
.body_ok(API_MESSAGE.PKG_CREATED);
|
||||
}
|
||||
|
||||
public whoami() {
|
||||
|
|
|
@ -14,7 +14,12 @@ export default class VerdaccioProcess implements IServerProcess {
|
|||
private isDebug: boolean;
|
||||
private silence: boolean;
|
||||
|
||||
public constructor(config: IVerdaccioConfig, bridge: IServerBridge, silence = true, isDebug = false) {
|
||||
public constructor(
|
||||
config: IVerdaccioConfig,
|
||||
bridge: IServerBridge,
|
||||
silence = true,
|
||||
isDebug = false
|
||||
) {
|
||||
this.config = config;
|
||||
this.bridge = bridge;
|
||||
this.silence = silence;
|
||||
|
|
|
@ -11,7 +11,11 @@ export function generateRamdonStorage() {
|
|||
return path.join(tempRoot, tempStorage);
|
||||
}
|
||||
|
||||
export function generateNewVersion(pkgName: string, version: string, shashum = '238e7641e59508dc9c20eb4ad37a8aa57ab777b4'): Version {
|
||||
export function generateNewVersion(
|
||||
pkgName: string,
|
||||
version: string,
|
||||
shashum = '238e7641e59508dc9c20eb4ad37a8aa57ab777b4'
|
||||
): Version {
|
||||
// $FlowFixMe
|
||||
return {
|
||||
name: pkgName,
|
||||
|
@ -30,7 +34,8 @@ export function generateNewVersion(pkgName: string, version: string, shashum = '
|
|||
name: 'Foo',
|
||||
},
|
||||
dist: {
|
||||
integrity: 'sha512-zVEqt1JUCOPsash9q4wMkJEDPD+QCx95TRhQII+JnoS31uBUKoZxhzvvUJCcLVy2CQG4QdwXARU7dYWPnrwhGg==',
|
||||
integrity:
|
||||
'sha512-zVEqt1JUCOPsash9q4wMkJEDPD+QCx95TRhQII+JnoS31uBUKoZxhzvvUJCcLVy2CQG4QdwXARU7dYWPnrwhGg==',
|
||||
shasum: shashum,
|
||||
tarball: `http:\/\/localhost:4873\/${pkgName}\/-\/${pkgName}-${version}.tgz`,
|
||||
},
|
||||
|
|
16
packages/node-api/src/bootstrap.d.ts
vendored
16
packages/node-api/src/bootstrap.d.ts
vendored
|
@ -8,6 +8,18 @@ import { Callback } from '@verdaccio/types';
|
|||
* @param {String} pkgVersion
|
||||
* @param {String} pkgName
|
||||
*/
|
||||
declare function startVerdaccio(config: any, cliListen: string, configPath: string, pkgVersion: string, pkgName: string, callback: Callback): void;
|
||||
declare function listenDefaultCallback(webServer: Application, addr: any, pkgName: string, pkgVersion: string): void;
|
||||
declare function startVerdaccio(
|
||||
config: any,
|
||||
cliListen: string,
|
||||
configPath: string,
|
||||
pkgVersion: string,
|
||||
pkgName: string,
|
||||
callback: Callback
|
||||
): void;
|
||||
declare function listenDefaultCallback(
|
||||
webServer: Application,
|
||||
addr: any,
|
||||
pkgName: string,
|
||||
pkgVersion: string
|
||||
): void;
|
||||
export { startVerdaccio, listenDefaultCallback };
|
||||
|
|
|
@ -14,7 +14,15 @@ import { logger } from '@verdaccio/logger';
|
|||
import { getListListenAddresses, resolveConfigPath } from './cli-utils';
|
||||
import { displayExperimentsInfoBox } from './experiments';
|
||||
|
||||
function launchServer(app, addr, config, configPath: string, pkgVersion: string, pkgName: string, callback: Callback): void {
|
||||
function launchServer(
|
||||
app,
|
||||
addr,
|
||||
config,
|
||||
configPath: string,
|
||||
pkgVersion: string,
|
||||
pkgName: string,
|
||||
callback: Callback
|
||||
): void {
|
||||
let webServer;
|
||||
if (addr.proto === 'https') {
|
||||
webServer = handleHTTPS(app, configPath, config);
|
||||
|
@ -22,7 +30,11 @@ function launchServer(app, addr, config, configPath: string, pkgVersion: string,
|
|||
// http
|
||||
webServer = http.createServer(app);
|
||||
}
|
||||
if (config.server && typeof config.server.keepAliveTimeout !== 'undefined' && config.server.keepAliveTimeout !== 'null') {
|
||||
if (
|
||||
config.server &&
|
||||
typeof config.server.keepAliveTimeout !== 'undefined' &&
|
||||
config.server.keepAliveTimeout !== 'null'
|
||||
) {
|
||||
// library definition for node is not up to date (doesn't contain recent 8.0 changes)
|
||||
webServer.keepAliveTimeout = config.server.keepAliveTimeout * 1000;
|
||||
}
|
||||
|
@ -39,7 +51,14 @@ function launchServer(app, addr, config, configPath: string, pkgVersion: string,
|
|||
* @param {String} pkgVersion
|
||||
* @param {String} pkgName
|
||||
*/
|
||||
function startVerdaccio(config: any, cliListen: string, configPath: string, pkgVersion: string, pkgName: string, callback: Callback): void {
|
||||
function startVerdaccio(
|
||||
config: any,
|
||||
cliListen: string,
|
||||
configPath: string,
|
||||
pkgVersion: string,
|
||||
pkgName: string,
|
||||
callback: Callback
|
||||
): void {
|
||||
if (isObject(config) === false) {
|
||||
throw new Error(API_ERROR.CONFIG_BAD_FORMAT);
|
||||
}
|
||||
|
@ -50,7 +69,9 @@ function startVerdaccio(config: any, cliListen: string, configPath: string, pkgV
|
|||
displayExperimentsInfoBox(config.experiments);
|
||||
}
|
||||
|
||||
addresses.forEach((addr) => launchServer(app, addr, config, configPath, pkgVersion, pkgName, callback));
|
||||
addresses.forEach((addr) =>
|
||||
launchServer(app, addr, config, configPath, pkgVersion, pkgName, callback)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -71,7 +92,10 @@ function logHTTPSWarning(storageLocation) {
|
|||
// commands are borrowed from node.js docs
|
||||
'To quickly create self-signed certificate, use:',
|
||||
' $ openssl genrsa -out ' + resolveConfigPath(storageLocation, keyPem) + ' 2048',
|
||||
' $ openssl req -new -sha256 -key ' + resolveConfigPath(storageLocation, keyPem) + ' -out ' + resolveConfigPath(storageLocation, csrPem),
|
||||
' $ openssl req -new -sha256 -key ' +
|
||||
resolveConfigPath(storageLocation, keyPem) +
|
||||
' -out ' +
|
||||
resolveConfigPath(storageLocation, csrPem),
|
||||
' $ openssl x509 -req -in ' +
|
||||
resolveConfigPath(storageLocation, csrPem) +
|
||||
' -signkey ' +
|
||||
|
@ -126,7 +150,12 @@ function handleHTTPS(app: Application, configPath: string, config: ConfigWithHtt
|
|||
}
|
||||
}
|
||||
|
||||
function listenDefaultCallback(webServer: Application, addr: any, pkgName: string, pkgVersion: string): void {
|
||||
function listenDefaultCallback(
|
||||
webServer: Application,
|
||||
addr: any,
|
||||
pkgName: string,
|
||||
pkgVersion: string
|
||||
): void {
|
||||
webServer
|
||||
.listen(addr.port || addr.path, addr.host, (): void => {
|
||||
// send a message for tests
|
||||
|
|
|
@ -48,7 +48,9 @@ export function getListListenAddresses(argListen: string, configListen: any): an
|
|||
if (!parsedAddr) {
|
||||
logger.logger.warn(
|
||||
{ addr: addr },
|
||||
'invalid address - @{addr}, we expect a port (e.g. "4873"),' + ' host:port (e.g. "localhost:4873") or full url' + ' (e.g. "http://localhost:4873/")'
|
||||
'invalid address - @{addr}, we expect a port (e.g. "4873"),' +
|
||||
' host:port (e.g. "localhost:4873") or full url' +
|
||||
' (e.g. "http://localhost:4873/")'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,13 @@ const logger = require('@verdaccio/logger');
|
|||
export function displayExperimentsInfoBox(experiments) {
|
||||
const experimentList = Object.keys(experiments);
|
||||
if (experimentList.length >= 1) {
|
||||
logger.logger.warn('⚠️ experiments are enabled, we recommend do not use experiments in production, comment out this section to disable it');
|
||||
logger.logger.warn(
|
||||
'⚠️ experiments are enabled, we recommend do not use experiments in production, comment out this section to disable it'
|
||||
);
|
||||
experimentList.forEach((experiment) => {
|
||||
logger.logger.warn(` - support for ${experiment} ${experiments[experiment] ? 'is enabled' : ' is disabled'}`);
|
||||
logger.logger.warn(
|
||||
` - support for ${experiment} ${experiments[experiment] ? 'is enabled' : ' is disabled'}`
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,10 @@ export function logHTTPSWarning(storageLocation) {
|
|||
// commands are borrowed from node.js docs
|
||||
'To quickly create self-signed certificate, use:',
|
||||
' $ openssl genrsa -out ' + resolveConfigPath(storageLocation, keyPem) + ' 2048',
|
||||
' $ openssl req -new -sha256 -key ' + resolveConfigPath(storageLocation, keyPem) + ' -out ' + resolveConfigPath(storageLocation, csrPem),
|
||||
' $ openssl req -new -sha256 -key ' +
|
||||
resolveConfigPath(storageLocation, keyPem) +
|
||||
' -out ' +
|
||||
resolveConfigPath(storageLocation, csrPem),
|
||||
' $ openssl x509 -req -in ' +
|
||||
resolveConfigPath(storageLocation, csrPem) +
|
||||
' -signkey ' +
|
||||
|
|
|
@ -35,18 +35,25 @@ describe('startServer via API', () => {
|
|||
const version = '1.0.0';
|
||||
const port = '6000';
|
||||
|
||||
await startVerdaccio(configExample(), port, store, version, serverName, (webServer, addrs, pkgName, pkgVersion) => {
|
||||
expect(webServer).toBeDefined();
|
||||
expect(addrs).toBeDefined();
|
||||
expect(addrs.proto).toBe(DEFAULT_PROTOCOL);
|
||||
expect(addrs.host).toBe(DEFAULT_DOMAIN);
|
||||
expect(addrs.port).toBe(port);
|
||||
expect(pkgName).toBeDefined();
|
||||
expect(pkgVersion).toBeDefined();
|
||||
expect(pkgVersion).toBe(version);
|
||||
expect(pkgName).toBe(serverName);
|
||||
done();
|
||||
});
|
||||
await startVerdaccio(
|
||||
configExample(),
|
||||
port,
|
||||
store,
|
||||
version,
|
||||
serverName,
|
||||
(webServer, addrs, pkgName, pkgVersion) => {
|
||||
expect(webServer).toBeDefined();
|
||||
expect(addrs).toBeDefined();
|
||||
expect(addrs.proto).toBe(DEFAULT_PROTOCOL);
|
||||
expect(addrs.host).toBe(DEFAULT_DOMAIN);
|
||||
expect(addrs.port).toBe(port);
|
||||
expect(pkgName).toBeDefined();
|
||||
expect(pkgVersion).toBeDefined();
|
||||
expect(pkgVersion).toBe(version);
|
||||
expect(pkgName).toBe(serverName);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('should set keepAliveTimeout to 0 seconds', async (done) => {
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
"request": "2.87.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/dev-types": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/types": "workspace:*"
|
||||
},
|
||||
"gitHead": "7c246ede52ff717707fcae66dd63fc4abd536982"
|
||||
|
|
|
@ -6,9 +6,25 @@ import _ from 'lodash';
|
|||
import request from 'request';
|
||||
import { parseInterval, isObject, ErrorCode, buildToken } from '@verdaccio/utils';
|
||||
import { ReadTarball } from '@verdaccio/streams';
|
||||
import { ERROR_CODE, TOKEN_BASIC, TOKEN_BEARER, HEADERS, HTTP_STATUS, API_ERROR, HEADER_TYPE, CHARACTER_ENCODING } from '@verdaccio/dev-commons';
|
||||
import { Config, Callback, Headers, Logger, Package } from '@verdaccio/types';
|
||||
import { IProxy, UpLinkConfLocal } from '@verdaccio/dev-types';
|
||||
import {
|
||||
ERROR_CODE,
|
||||
TOKEN_BASIC,
|
||||
TOKEN_BEARER,
|
||||
HEADERS,
|
||||
HTTP_STATUS,
|
||||
API_ERROR,
|
||||
HEADER_TYPE,
|
||||
CHARACTER_ENCODING,
|
||||
} from '@verdaccio/dev-commons';
|
||||
import {
|
||||
Config,
|
||||
Callback,
|
||||
Headers,
|
||||
Logger,
|
||||
UpLinkConf,
|
||||
Package,
|
||||
IReadTarball,
|
||||
} from '@verdaccio/types';
|
||||
const LoggerApi = require('@verdaccio/logger');
|
||||
|
||||
const encode = function (thing): string {
|
||||
|
@ -25,6 +41,33 @@ const setConfig = (config, key, def): string => {
|
|||
return _.isNil(config[key]) === false ? config[key] : def;
|
||||
};
|
||||
|
||||
export type UpLinkConfLocal = UpLinkConf & {
|
||||
no_proxy?: string;
|
||||
};
|
||||
|
||||
export interface ProxyList {
|
||||
[key: string]: IProxy;
|
||||
}
|
||||
|
||||
export interface IProxy {
|
||||
config: UpLinkConfLocal;
|
||||
failed_requests: number;
|
||||
userAgent: string;
|
||||
ca?: string | void;
|
||||
logger: Logger;
|
||||
server_id: string;
|
||||
url: any;
|
||||
maxage: number;
|
||||
timeout: number;
|
||||
max_fails: number;
|
||||
fail_timeout: number;
|
||||
upname: string;
|
||||
fetchTarball(url: string): IReadTarball;
|
||||
isUplinkValid(url: string): boolean;
|
||||
search(options: any);
|
||||
getRemoteMetadata(name: string, options: any, callback: Callback): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Storage interface
|
||||
* (same for storage.js, local-storage.js, up-storage.js)
|
||||
|
@ -401,8 +444,11 @@ class ProxyStorage implements IProxy {
|
|||
public isUplinkValid(url: string): boolean {
|
||||
// $FlowFixMe
|
||||
const urlParsed: UrlWithStringQuery = URL.parse(url);
|
||||
const isHTTPS = (urlDomainParsed: URL): boolean => urlDomainParsed.protocol === 'https:' && (urlParsed.port === null || urlParsed.port === '443');
|
||||
const getHost = (urlDomainParsed): boolean => (isHTTPS(urlDomainParsed) ? urlDomainParsed.hostname : urlDomainParsed.host);
|
||||
const isHTTPS = (urlDomainParsed: URL): boolean =>
|
||||
urlDomainParsed.protocol === 'https:' &&
|
||||
(urlParsed.port === null || urlParsed.port === '443');
|
||||
const getHost = (urlDomainParsed): boolean =>
|
||||
isHTTPS(urlDomainParsed) ? urlDomainParsed.hostname : urlDomainParsed.host;
|
||||
const isMatchProtocol: boolean = urlParsed.protocol === this.url.protocol;
|
||||
const isMatchHost: boolean = getHost(urlParsed) === getHost(this.url);
|
||||
// @ts-ignore
|
||||
|
@ -439,7 +485,9 @@ class ProxyStorage implements IProxy {
|
|||
return callback(ErrorCode.getNotFound(API_ERROR.NOT_PACKAGE_UPLINK));
|
||||
}
|
||||
if (!(res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)) {
|
||||
const error = ErrorCode.getInternalError(`${API_ERROR.BAD_STATUS_CODE}: ${res.statusCode}`);
|
||||
const error = ErrorCode.getInternalError(
|
||||
`${API_ERROR.BAD_STATUS_CODE}: ${res.statusCode}`
|
||||
);
|
||||
// $FlowFixMe
|
||||
error.remoteStatus = res.statusCode;
|
||||
return callback(error);
|
||||
|
@ -473,7 +521,10 @@ class ProxyStorage implements IProxy {
|
|||
return stream.emit('error', ErrorCode.getNotFound(API_ERROR.NOT_FILE_UPLINK));
|
||||
}
|
||||
if (!(res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)) {
|
||||
return stream.emit('error', ErrorCode.getInternalError(`bad uplink status code: ${res.statusCode}`));
|
||||
return stream.emit(
|
||||
'error',
|
||||
ErrorCode.getInternalError(`bad uplink status code: ${res.statusCode}`)
|
||||
);
|
||||
}
|
||||
if (res.headers[HEADER_TYPE.CONTENT_LENGTH]) {
|
||||
expected_length = res.headers[HEADER_TYPE.CONTENT_LENGTH];
|
||||
|
@ -523,7 +574,10 @@ class ProxyStorage implements IProxy {
|
|||
|
||||
requestStream.on('response', (res): void => {
|
||||
if (!String(res.statusCode).match(/^2\d\d$/)) {
|
||||
return transformStream.emit('error', ErrorCode.getInternalError(`bad status code ${res.statusCode} from uplink`));
|
||||
return transformStream.emit(
|
||||
'error',
|
||||
ErrorCode.getInternalError(`bad status code ${res.statusCode} from uplink`)
|
||||
);
|
||||
}
|
||||
|
||||
// See https://github.com/request/request#requestoptions-callback
|
||||
|
@ -572,7 +626,9 @@ class ProxyStorage implements IProxy {
|
|||
// FIXME: proxy logic is odd, something is wrong here.
|
||||
// @ts-ignore
|
||||
if (!this.proxy) {
|
||||
headers['X-Forwarded-For'] = (req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'] + ', ' : '') + req.connection.remoteAddress;
|
||||
headers['X-Forwarded-For'] =
|
||||
(req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'] + ', ' : '') +
|
||||
req.connection.remoteAddress;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -622,7 +678,10 @@ class ProxyStorage implements IProxy {
|
|||
* @private
|
||||
*/
|
||||
private _ifRequestFailure(): boolean {
|
||||
return this.failed_requests >= this.max_fails && Math.abs(Date.now() - (this.last_request_time as number)) < this.fail_timeout;
|
||||
return (
|
||||
this.failed_requests >= this.max_fails &&
|
||||
Math.abs(Date.now() - (this.last_request_time as number)) < this.fail_timeout
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -632,7 +691,12 @@ class ProxyStorage implements IProxy {
|
|||
* @param {*} mainconfig
|
||||
* @param {*} isHTTPS
|
||||
*/
|
||||
private _setupProxy(hostname: string, config: UpLinkConfLocal, mainconfig: Config, isHTTPS: boolean): void {
|
||||
private _setupProxy(
|
||||
hostname: string,
|
||||
config: UpLinkConfLocal,
|
||||
mainconfig: Config,
|
||||
isHTTPS: boolean
|
||||
): void {
|
||||
let noProxyList;
|
||||
const proxy_key: string = isHTTPS ? 'https_proxy' : 'http_proxy';
|
||||
|
||||
|
@ -667,7 +731,10 @@ class ProxyStorage implements IProxy {
|
|||
}
|
||||
if (hostname.lastIndexOf(noProxyItem) === hostname.length - noProxyItem.length) {
|
||||
if (this.proxy) {
|
||||
this.logger.debug({ url: this.url.href, rule: noProxyItem }, 'not using proxy for @{url}, excluded by @{rule} rule');
|
||||
this.logger.debug(
|
||||
{ url: this.url.href, rule: noProxyItem },
|
||||
'not using proxy for @{url}, excluded by @{rule} rule'
|
||||
);
|
||||
// @ts-ignore
|
||||
this.proxy = false;
|
||||
}
|
||||
|
@ -680,7 +747,10 @@ class ProxyStorage implements IProxy {
|
|||
if (_.isString(this.proxy) === false) {
|
||||
delete this.proxy;
|
||||
} else {
|
||||
this.logger.debug({ url: this.url.href, proxy: this.proxy }, 'using proxy @{proxy} for @{url}');
|
||||
this.logger.debug(
|
||||
{ url: this.url.href, proxy: this.proxy },
|
||||
'using proxy @{proxy} for @{url}'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Versions, Config } from '@verdaccio/types';
|
||||
import { IProxy, ProxyList } from '@verdaccio/dev-types';
|
||||
|
||||
import { ProxyStorage } from './up-storage';
|
||||
import { ProxyStorage, IProxy, ProxyList } from './up-storage';
|
||||
|
||||
/**
|
||||
* Set up the Up Storage for each link.
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import { buildToken } from '@verdaccio/utils';
|
||||
|
||||
import { ERROR_CODE, TOKEN_BASIC, TOKEN_BEARER, DEFAULT_REGISTRY, HEADERS } from '@verdaccio/dev-commons';
|
||||
import {
|
||||
ERROR_CODE,
|
||||
TOKEN_BASIC,
|
||||
TOKEN_BEARER,
|
||||
DEFAULT_REGISTRY,
|
||||
HEADERS,
|
||||
} from '@verdaccio/dev-commons';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
|
||||
import { ProxyStorage } from '../src/up-storage';
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/commons-api": "workspace:*",
|
||||
"@verdaccio/dev-types": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/mock": "workspace:5.0.0-alpha.0",
|
||||
"@verdaccio/proxy": "workspace:5.0.0-alpha.0",
|
||||
"http-errors": "1.7.3",
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import _ from 'lodash';
|
||||
import { Application } from 'express';
|
||||
import { $ResponseExtend, $RequestExtend, $NextFunctionVer } from '@verdaccio/dev-types';
|
||||
import { $ResponseExtend, $RequestExtend, $NextFunctionVer } from '../../types/custom';
|
||||
|
||||
export default (app: Application, selfPath: string): void => {
|
||||
// Hook for tests only
|
||||
app.get('/-/_debug', function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
app.get('/-/_debug', function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
const doGarbabeCollector = _.isNil(global.gc) === false;
|
||||
|
||||
if (doGarbabeCollector) {
|
||||
|
|
|
@ -14,10 +14,12 @@ import { Config as AppConfig } from '@verdaccio/config';
|
|||
|
||||
import { webAPI, renderWebMiddleware } from '@verdaccio/web';
|
||||
|
||||
import { $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler, IAuth } from '@verdaccio/dev-types';
|
||||
import { IAuth } from '@verdaccio/auth';
|
||||
import { IStorageHandler } from '@verdaccio/store';
|
||||
import { Config as IConfig, IPluginMiddleware, IPluginStorageFilter } from '@verdaccio/types';
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
import { log, final, errorReportingMiddleware } from '@verdaccio/middleware';
|
||||
import { $ResponseExtend, $RequestExtend, $NextFunctionVer } from '../types/custom';
|
||||
|
||||
import hookDebug from './debug';
|
||||
|
||||
|
@ -39,7 +41,11 @@ const defineAPI = function (config: IConfig, storage: IStorageHandler): any {
|
|||
|
||||
app.use(compression());
|
||||
|
||||
app.get('/favicon.ico', function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
app.get('/favicon.ico', function (
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
): void {
|
||||
req.url = '/-/static/favicon.png';
|
||||
next();
|
||||
});
|
||||
|
@ -55,14 +61,20 @@ const defineAPI = function (config: IConfig, storage: IStorageHandler): any {
|
|||
logger: logger,
|
||||
};
|
||||
|
||||
const plugins: IPluginMiddleware<IConfig>[] = loadPlugin(config, config.middlewares, plugin_params, function (plugin: IPluginMiddleware<IConfig>) {
|
||||
return plugin.register_middlewares;
|
||||
});
|
||||
const plugins: IPluginMiddleware<IConfig>[] = loadPlugin(
|
||||
config,
|
||||
config.middlewares,
|
||||
plugin_params,
|
||||
function (plugin: IPluginMiddleware<IConfig>) {
|
||||
return plugin.register_middlewares;
|
||||
}
|
||||
);
|
||||
plugins.forEach((plugin: IPluginMiddleware<IConfig>) => {
|
||||
plugin.register_middlewares(app, auth, storage);
|
||||
});
|
||||
|
||||
// For npm request
|
||||
// @ts-ignore
|
||||
app.use(apiEndpoint(config, auth, storage));
|
||||
|
||||
// For WebUI & WebUI API
|
||||
|
@ -80,7 +92,12 @@ const defineAPI = function (config: IConfig, storage: IStorageHandler): any {
|
|||
next(ErrorCode.getNotFound(API_ERROR.FILE_NOT_FOUND));
|
||||
});
|
||||
|
||||
app.use(function (err: HttpError, req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
app.use(function (
|
||||
err: HttpError,
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
) {
|
||||
if (_.isError(err)) {
|
||||
if (err.code === 'ECONNABORT' && res.statusCode === HTTP_STATUS.NOT_MODIFIED) {
|
||||
return next();
|
||||
|
@ -110,7 +127,12 @@ export default (async function (configHash: any): Promise<any> {
|
|||
config: config,
|
||||
logger: logger,
|
||||
};
|
||||
const filters = loadPlugin(config, config.filters || {}, plugin_params, (plugin: IPluginStorageFilter<IConfig>) => plugin.filter_metadata);
|
||||
const filters = loadPlugin(
|
||||
config,
|
||||
config.filters || {},
|
||||
plugin_params,
|
||||
(plugin: IPluginStorageFilter<IConfig>) => plugin.filter_metadata
|
||||
);
|
||||
const storage: IStorageHandler = new Storage(config);
|
||||
// waits until init calls have been initialized
|
||||
await storage.init(config, filters);
|
||||
|
|
|
@ -30,7 +30,8 @@ const json = {
|
|||
_nodeVersion: '8.7.0',
|
||||
_npmUser: {},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
integrity:
|
||||
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9',
|
||||
tarball: 'http://localhost:5555/@scope/pk1-test/-/@scope/pk1-test-1.0.6.tgz',
|
||||
},
|
||||
|
|
|
@ -26,7 +26,8 @@ export function generateVersion(pkgName, version) {
|
|||
name: 'foo',
|
||||
},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
integrity:
|
||||
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
||||
tarball: `http:\/\/localhost:5555\/${pkgName}\/-\/${pkgName}-${version}.tgz`,
|
||||
},
|
||||
|
@ -39,7 +40,10 @@ export function generateVersion(pkgName, version) {
|
|||
* @param pkgName
|
||||
* @param _versions
|
||||
*/
|
||||
export function generatePackageUnpublish(pkgName: string, _versions: string[] = ['1.0.0']): Package {
|
||||
export function generatePackageUnpublish(
|
||||
pkgName: string,
|
||||
_versions: string[] = ['1.0.0']
|
||||
): Package {
|
||||
const latest: string = _versions[_versions.length - 1];
|
||||
const versions = _versions.reduce((cat, version) => {
|
||||
cat[version] = generateVersion(pkgName, version);
|
||||
|
@ -102,7 +106,8 @@ export function generatePackageMetadata(pkgName: string, version = '1.0.0'): Pac
|
|||
name: 'foo',
|
||||
},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
integrity:
|
||||
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
||||
tarball: `http:\/\/localhost:5555\/${pkgName}\/-\/${pkgName}-${version}.tgz`,
|
||||
},
|
||||
|
@ -120,7 +125,11 @@ export function generatePackageMetadata(pkgName: string, version = '1.0.0'): Pac
|
|||
};
|
||||
}
|
||||
|
||||
export function generateDeprecateMetadata(pkgName: string, version = '1.0.0', deprecated: string = ''): Package {
|
||||
export function generateDeprecateMetadata(
|
||||
pkgName: string,
|
||||
version = '1.0.0',
|
||||
deprecated: string = ''
|
||||
): Package {
|
||||
const res = {
|
||||
...generatePackageMetadata(pkgName, version),
|
||||
_attachments: {},
|
||||
|
|
|
@ -2,7 +2,13 @@ import path from 'path';
|
|||
import request from 'supertest';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { HEADERS, HTTP_STATUS, HEADER_TYPE, API_MESSAGE, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
import {
|
||||
HEADERS,
|
||||
HTTP_STATUS,
|
||||
HEADER_TYPE,
|
||||
API_MESSAGE,
|
||||
TOKEN_BEARER,
|
||||
} from '@verdaccio/dev-commons';
|
||||
import { buildToken, encodeScopedUri } from '@verdaccio/utils';
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
|
@ -21,7 +27,13 @@ import {
|
|||
|
||||
import endPointAPI from '../../src';
|
||||
import publishMetadata from './helpers/publish-api';
|
||||
import { generateDeprecateMetadata, generatePackageMetadata, generatePackageUnpublish, generateStarMedatada, generateVersion } from './helpers/utils';
|
||||
import {
|
||||
generateDeprecateMetadata,
|
||||
generatePackageMetadata,
|
||||
generatePackageUnpublish,
|
||||
generateStarMedatada,
|
||||
generateVersion,
|
||||
} from './helpers/utils';
|
||||
|
||||
setup([]);
|
||||
|
||||
|
@ -91,7 +103,9 @@ describe('endpoint unit test', () => {
|
|||
.expect(HTTP_STATUS.FORBIDDEN)
|
||||
.end(function (err, res) {
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(/authorization required to access package auth-package/);
|
||||
expect(res.body.error).toMatch(
|
||||
/authorization required to access package auth-package/
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -104,7 +118,9 @@ describe('endpoint unit test', () => {
|
|||
.expect(HTTP_STATUS.FORBIDDEN)
|
||||
.end(function (err, res) {
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(/authorization required to access package auth-package/);
|
||||
expect(res.body.error).toMatch(
|
||||
/authorization required to access package auth-package/
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -117,7 +133,9 @@ describe('endpoint unit test', () => {
|
|||
.expect(HTTP_STATUS.FORBIDDEN)
|
||||
.end(function (err, res) {
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(/authorization required to access package auth-package/);
|
||||
expect(res.body.error).toMatch(
|
||||
/authorization required to access package auth-package/
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -470,14 +488,24 @@ describe('endpoint unit test', () => {
|
|||
}
|
||||
|
||||
const newVersion = '2.0.1';
|
||||
const [newErr] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`, generatePackageMetadata(pkgName, newVersion), token);
|
||||
const [newErr] = await putPackage(
|
||||
request(app),
|
||||
`/${encodeScopedUri(pkgName)}`,
|
||||
generatePackageMetadata(pkgName, newVersion),
|
||||
token
|
||||
);
|
||||
if (newErr) {
|
||||
expect(newErr).toBeNull();
|
||||
return done(newErr);
|
||||
}
|
||||
|
||||
const deletePayload = generatePackageUnpublish(pkgName, ['2.0.0']);
|
||||
const [err2, res2] = await putPackage(request(app), generateUnPublishURI(pkgName), deletePayload, token);
|
||||
const [err2, res2] = await putPackage(
|
||||
request(app),
|
||||
generateUnPublishURI(pkgName),
|
||||
deletePayload,
|
||||
token
|
||||
);
|
||||
|
||||
expect(err2).toBeNull();
|
||||
expect(res2.body.ok).toMatch(API_MESSAGE.PKG_CHANGED);
|
||||
|
@ -526,17 +554,29 @@ describe('endpoint unit test', () => {
|
|||
const newVersion = '1.0.0';
|
||||
const token = await getNewToken(request(app), credentials);
|
||||
|
||||
const [newErr] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`, generatePackageMetadata(pkgName, newVersion), token);
|
||||
const [newErr] = await putPackage(
|
||||
request(app),
|
||||
`/${encodeScopedUri(pkgName)}`,
|
||||
generatePackageMetadata(pkgName, newVersion),
|
||||
token
|
||||
);
|
||||
if (newErr) {
|
||||
expect(newErr).toBeNull();
|
||||
return done(newErr);
|
||||
}
|
||||
|
||||
const deletePayload = generatePackageUnpublish(pkgName, ['2.0.0']);
|
||||
const [err2, res2] = await putPackage(request(app), generateUnPublishURI(pkgName), deletePayload, token);
|
||||
const [err2, res2] = await putPackage(
|
||||
request(app),
|
||||
generateUnPublishURI(pkgName),
|
||||
deletePayload,
|
||||
token
|
||||
);
|
||||
|
||||
expect(err2).not.toBeNull();
|
||||
expect(res2.body.error).toMatch(/user jota_unpublish_fail is not allowed to unpublish package non-unpublish/);
|
||||
expect(res2.body.error).toMatch(
|
||||
/user jota_unpublish_fail is not allowed to unpublish package non-unpublish/
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -562,10 +602,17 @@ describe('endpoint unit test', () => {
|
|||
const newVersion = '1.0.0';
|
||||
const token = await getNewToken(request(app), credentials);
|
||||
|
||||
const [newErr, resp] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`, generatePackageMetadata(pkgName, newVersion), token);
|
||||
const [newErr, resp] = await putPackage(
|
||||
request(app),
|
||||
`/${encodeScopedUri(pkgName)}`,
|
||||
generatePackageMetadata(pkgName, newVersion),
|
||||
token
|
||||
);
|
||||
|
||||
expect(newErr).not.toBeNull();
|
||||
expect(resp.body.error).toMatch(/user jota_only_unpublish_fail is not allowed to publish package only-unpublish/);
|
||||
expect(resp.body.error).toMatch(
|
||||
/user jota_only_unpublish_fail is not allowed to publish package only-unpublish/
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -741,7 +788,12 @@ describe('endpoint unit test', () => {
|
|||
let token = '';
|
||||
beforeAll(async (done) => {
|
||||
token = await getNewToken(request(app), credentials);
|
||||
await putPackage(request(app), `/${pkgName}`, generatePackageMetadata(pkgName, version), token);
|
||||
await putPackage(
|
||||
request(app),
|
||||
`/${pkgName}`,
|
||||
generatePackageMetadata(pkgName, version),
|
||||
token
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -775,20 +827,39 @@ describe('endpoint unit test', () => {
|
|||
let credentials = { name: 'only_publish', password: 'secretPass' };
|
||||
let token = await getNewToken(request(app), credentials);
|
||||
const pkg = generateDeprecateMetadata(pkgName, version, 'get deprecated');
|
||||
const [err, res] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`, pkg, token);
|
||||
const [err, res] = await putPackage(
|
||||
request(app),
|
||||
`/${encodeScopedUri(pkgName)}`,
|
||||
pkg,
|
||||
token
|
||||
);
|
||||
expect(err).not.toBeNull();
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(/user only_publish is not allowed to unpublish package @scope\/deprecate/);
|
||||
expect(res.body.error).toMatch(
|
||||
/user only_publish is not allowed to unpublish package @scope\/deprecate/
|
||||
);
|
||||
credentials = { name: 'only_unpublish', password: 'secretPass' };
|
||||
token = await getNewToken(request(app), credentials);
|
||||
const [err2, res2] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`, pkg, token);
|
||||
const [err2, res2] = await putPackage(
|
||||
request(app),
|
||||
`/${encodeScopedUri(pkgName)}`,
|
||||
pkg,
|
||||
token
|
||||
);
|
||||
expect(err2).not.toBeNull();
|
||||
expect(res2.body.error).toBeDefined();
|
||||
expect(res2.body.error).toMatch(/user only_unpublish is not allowed to publish package @scope\/deprecate/);
|
||||
expect(res2.body.error).toMatch(
|
||||
/user only_unpublish is not allowed to publish package @scope\/deprecate/
|
||||
);
|
||||
});
|
||||
|
||||
test('should deprecate multiple packages', async (done) => {
|
||||
await putPackage(request(app), `/${pkgName}`, generatePackageMetadata(pkgName, '1.0.1'), token);
|
||||
await putPackage(
|
||||
request(app),
|
||||
`/${pkgName}`,
|
||||
generatePackageMetadata(pkgName, '1.0.1'),
|
||||
token
|
||||
);
|
||||
const pkg = generateDeprecateMetadata(pkgName, version, 'get deprecated');
|
||||
pkg.versions['1.0.1'] = {
|
||||
...generateVersion(pkgName, '1.0.1'),
|
||||
|
|
|
@ -1,10 +1,23 @@
|
|||
import path from 'path';
|
||||
import request from 'supertest';
|
||||
|
||||
import { HEADERS, HTTP_STATUS, HEADER_TYPE, TOKEN_BEARER, TOKEN_BASIC, API_ERROR } from '@verdaccio/dev-commons';
|
||||
import {
|
||||
HEADERS,
|
||||
HTTP_STATUS,
|
||||
HEADER_TYPE,
|
||||
TOKEN_BEARER,
|
||||
TOKEN_BASIC,
|
||||
API_ERROR,
|
||||
} from '@verdaccio/dev-commons';
|
||||
import { mockServer, generateRamdonStorage } from '@verdaccio/mock';
|
||||
import { buildUserBuffer, buildToken } from '@verdaccio/utils';
|
||||
import { configExample, DOMAIN_SERVERS, addUser, getPackage, loginUserToken } from '@verdaccio/mock';
|
||||
import {
|
||||
configExample,
|
||||
DOMAIN_SERVERS,
|
||||
addUser,
|
||||
getPackage,
|
||||
loginUserToken,
|
||||
} from '@verdaccio/mock';
|
||||
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
|
@ -72,7 +85,12 @@ describe('endpoint user auth JWT unit test', () => {
|
|||
expect(resp1.body).toBeDefined();
|
||||
expect(resp1.body.name).toMatch('vue');
|
||||
|
||||
const [err2, resp2] = await getPackage(request(app), FAKE_TOKEN, 'vue', HTTP_STATUS.UNAUTHORIZED);
|
||||
const [err2, resp2] = await getPackage(
|
||||
request(app),
|
||||
FAKE_TOKEN,
|
||||
'vue',
|
||||
HTTP_STATUS.UNAUTHORIZED
|
||||
);
|
||||
expect(err2).toBeNull();
|
||||
expect(resp2.statusCode).toBe(HTTP_STATUS.UNAUTHORIZED);
|
||||
expect(resp2.body.error).toMatch(FORBIDDEN_VUE);
|
||||
|
@ -106,7 +124,12 @@ describe('endpoint user auth JWT unit test', () => {
|
|||
});
|
||||
|
||||
test('should fails on try to access with corrupted token', async (done) => {
|
||||
const [err2, resp2] = await getPackage(request(app), FAKE_TOKEN, 'vue', HTTP_STATUS.UNAUTHORIZED);
|
||||
const [err2, resp2] = await getPackage(
|
||||
request(app),
|
||||
FAKE_TOKEN,
|
||||
'vue',
|
||||
HTTP_STATUS.UNAUTHORIZED
|
||||
);
|
||||
expect(err2).toBeNull();
|
||||
expect(resp2.statusCode).toBe(HTTP_STATUS.UNAUTHORIZED);
|
||||
expect(resp2.body.error).toMatch(FORBIDDEN_VUE);
|
||||
|
@ -126,7 +149,13 @@ describe('endpoint user auth JWT unit test', () => {
|
|||
|
||||
// we login when token is valid
|
||||
const newCredentials = { name: 'newFailsUser', password: 'BAD_PASSWORD' };
|
||||
const [err2, resp2] = await loginUserToken(request(app), newCredentials.name, newCredentials, token, HTTP_STATUS.UNAUTHORIZED);
|
||||
const [err2, resp2] = await loginUserToken(
|
||||
request(app),
|
||||
newCredentials.name,
|
||||
newCredentials,
|
||||
token,
|
||||
HTTP_STATUS.UNAUTHORIZED
|
||||
);
|
||||
expect(err2).toBeNull();
|
||||
expect(resp2.statusCode).toBe(HTTP_STATUS.UNAUTHORIZED);
|
||||
expect(resp2.body.error).toMatch(API_ERROR.BAD_USERNAME_PASSWORD);
|
||||
|
|
|
@ -5,7 +5,14 @@ import { mockServer } from '@verdaccio/mock';
|
|||
import { API_ERROR, HTTP_STATUS, SUPPORT_ERRORS } from '@verdaccio/dev-commons';
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
import { generateRamdonStorage, getNewToken, getProfile, postProfile, configExample, DOMAIN_SERVERS } from '@verdaccio/mock';
|
||||
import {
|
||||
generateRamdonStorage,
|
||||
getNewToken,
|
||||
getProfile,
|
||||
postProfile,
|
||||
configExample,
|
||||
DOMAIN_SERVERS,
|
||||
} from '@verdaccio/mock';
|
||||
|
||||
import endPointAPI from '../../src';
|
||||
|
||||
|
@ -99,7 +106,12 @@ describe('endpoint user profile', () => {
|
|||
tfa: {},
|
||||
};
|
||||
const token = await getNewToken(request(app), credentials);
|
||||
const [, resp] = await postProfile(request(app), body, token, HTTP_STATUS.SERVICE_UNAVAILABLE);
|
||||
const [, resp] = await postProfile(
|
||||
request(app),
|
||||
body,
|
||||
token,
|
||||
HTTP_STATUS.SERVICE_UNAVAILABLE
|
||||
);
|
||||
|
||||
expect(resp.error).not.toBeNull();
|
||||
expect(resp.error.text).toMatch(SUPPORT_ERRORS.TFA_DISABLED);
|
||||
|
|
|
@ -4,7 +4,7 @@ import _ from 'lodash';
|
|||
import { Config as AppConfig } from '@verdaccio/config';
|
||||
import { Config, UpLinkConf } from '@verdaccio/types';
|
||||
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
import { IProxy } from '@verdaccio/dev-types';
|
||||
import { IProxy } from '@verdaccio/proxy';
|
||||
import { API_ERROR, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import { mockServer, configExample, DOMAIN_SERVERS } from '@verdaccio/mock';
|
||||
import { ProxyStorage } from '@verdaccio/proxy';
|
||||
|
@ -154,7 +154,10 @@ describe('UpStorge', () => {
|
|||
|
||||
describe('UpStorge::isUplinkValid', () => {
|
||||
describe('valid use cases', () => {
|
||||
const validateUpLink = (url: string, tarBallUrl = `${url}/artifactory/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz`) => {
|
||||
const validateUpLink = (
|
||||
url: string,
|
||||
tarBallUrl = `${url}/artifactory/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz`
|
||||
) => {
|
||||
const uplinkConf = { url };
|
||||
const proxy: IProxy = generateProxy(uplinkConf);
|
||||
|
||||
|
@ -184,7 +187,12 @@ describe('UpStorge', () => {
|
|||
// 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);
|
||||
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', () => {
|
||||
|
@ -229,7 +237,8 @@ describe('UpStorge', () => {
|
|||
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 tarBallUrl =
|
||||
'https://subdomain.domain:4000/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz';
|
||||
const uplinkConf = { url };
|
||||
const proxy: IProxy = generateProxy(uplinkConf);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import fs from 'fs';
|
|||
import { Writable } from 'stream';
|
||||
import { Config as AppConfig } from '@verdaccio/config';
|
||||
import { Storage } from '@verdaccio/store';
|
||||
import { IStorageHandler } from '@verdaccio/dev-types';
|
||||
import { IStorageHandler } from '@verdaccio/store';
|
||||
|
||||
import { Config } from '@verdaccio/types';
|
||||
import { API_ERROR, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
|
@ -139,7 +139,9 @@ describe('StorageTest', () => {
|
|||
reader.on('end', () => {
|
||||
expect(cachedSpy).toHaveBeenCalledTimes(0);
|
||||
expect(notcachedSpy).toHaveBeenCalledTimes(1);
|
||||
expect(notcachedSpy).toHaveBeenCalledWith('http://0.0.0.0:55548/@jquery%2fjquery/-/jquery-1.5.1.tgz');
|
||||
expect(notcachedSpy).toHaveBeenCalledWith(
|
||||
'http://0.0.0.0:55548/@jquery%2fjquery/-/jquery-1.5.1.tgz'
|
||||
);
|
||||
res();
|
||||
});
|
||||
reader.on('error', (err) => {
|
||||
|
|
|
@ -2,10 +2,23 @@ import path from 'path';
|
|||
import request from 'supertest';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { HEADERS, HTTP_STATUS, HEADER_TYPE, TOKEN_BEARER, API_ERROR, SUPPORT_ERRORS } from '@verdaccio/dev-commons';
|
||||
import {
|
||||
HEADERS,
|
||||
HTTP_STATUS,
|
||||
HEADER_TYPE,
|
||||
TOKEN_BEARER,
|
||||
API_ERROR,
|
||||
SUPPORT_ERRORS,
|
||||
} from '@verdaccio/dev-commons';
|
||||
|
||||
import { buildToken } from '@verdaccio/utils';
|
||||
import { generateRamdonStorage, DOMAIN_SERVERS, mockServer, getNewToken, configExample } from '@verdaccio/mock';
|
||||
import {
|
||||
generateRamdonStorage,
|
||||
DOMAIN_SERVERS,
|
||||
mockServer,
|
||||
getNewToken,
|
||||
configExample,
|
||||
} from '@verdaccio/mock';
|
||||
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
|
|
|
@ -3,7 +3,13 @@ import request from 'supertest';
|
|||
|
||||
import { HEADERS, API_ERROR, HTTP_STATUS, HEADER_TYPE, DIST_TAGS } from '@verdaccio/dev-commons';
|
||||
|
||||
import { addUser, mockServer, DOMAIN_SERVERS, configExample, generateRamdonStorage } from '@verdaccio/mock';
|
||||
import {
|
||||
addUser,
|
||||
mockServer,
|
||||
DOMAIN_SERVERS,
|
||||
configExample,
|
||||
generateRamdonStorage,
|
||||
} from '@verdaccio/mock';
|
||||
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
import endPointAPI from '../../src';
|
||||
|
@ -58,7 +64,11 @@ describe('endpoint web unit test', () => {
|
|||
.send(JSON.stringify(publishMetadata))
|
||||
.expect(HTTP_STATUS.CREATED);
|
||||
|
||||
await request(app).put('/forbidden-place').set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON).send(JSON.stringify(forbiddenPlace)).expect(HTTP_STATUS.CREATED);
|
||||
await request(app)
|
||||
.put('/forbidden-place')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(forbiddenPlace))
|
||||
.expect(HTTP_STATUS.CREATED);
|
||||
});
|
||||
|
||||
describe('Packages', () => {
|
||||
|
|
|
@ -30,7 +30,8 @@ const json = {
|
|||
_nodeVersion: '8.7.0',
|
||||
_npmUser: {},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
integrity:
|
||||
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9',
|
||||
tarball: 'http://localhost:5555/forbidden-place/-/forbidden-place-1.0.6.tgz',
|
||||
},
|
||||
|
|
|
@ -30,7 +30,8 @@ const json = {
|
|||
_nodeVersion: '8.7.0',
|
||||
_npmUser: {},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
integrity:
|
||||
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9',
|
||||
tarball: 'http://localhost:5555/@scope/pk1-test/-/@scope/pk1-test-1.0.6.tgz',
|
||||
},
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue