Skip to content

Commit

Permalink
feat: improved config and token signature (#3658)
Browse files Browse the repository at this point in the history
* chore: add signature module

* feat: improved config and token signature

feat: improved config and token signature

Update package.json

* chore: update deps

* types

* fix: logger missing options

* chore: update deps

* update dep

* Update e2e-jest-workflow.yml

* Update init.ts

* Update config-path.ts

* fix logger

* Update init.ts
  • Loading branch information
juanpicado committed Mar 3, 2023
1 parent 8c8dafc commit e50d4d9
Show file tree
Hide file tree
Showing 43 changed files with 218 additions and 769 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/e2e-jest-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ jobs:
echo "const leftPad = require('left-pad'); it('should resolve a module', () => { expect(typeof leftPad).toBe('function');});" | tee module.test.js
yarn jest module.test.js
pnpm7:
name: 'pnpm:next-7:jest example'
name: 'pnpm:7:jest example'
runs-on: ubuntu-latest

steps:
Expand All @@ -217,12 +217,12 @@ jobs:
with:
node-version: 16.x
- name: 'install latest pnpm'
run: npm i -g pnpm@next-7
run: npm i -g pnpm@latest-7
- name: Install Dependencies
run: yarn install
- name: 'Run verdaccio in the background'
run: |
nohup yarn node ./scripts/run-verdaccio.js &
yarn node ./scripts/run-verdaccio.js &
- name: 'Ping to verdaccio'
run: |
pnpm ping --registry http://localhost:4873
Expand Down
212 changes: 87 additions & 125 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
28 changes: 17 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@
"url": "https://opencollective.com/verdaccio"
},
"dependencies": {
"@verdaccio/config": "6.0.0-6-next.62",
"@verdaccio/core": "6.0.0-6-next.62",
"@verdaccio/config": "6.0.0-6-next.63",
"@verdaccio/core": "6.0.0-6-next.63",
"@verdaccio/local-storage": "10.3.1",
"@verdaccio/logger-7": "6.0.0-6-next.7",
"@verdaccio/middleware": "6.0.0-6-next.41",
"@verdaccio/logger-7": "6.0.0-6-next.8",
"@verdaccio/middleware": "6.0.0-6-next.42",
"@verdaccio/signature": "6.0.0-6-next.2",
"@verdaccio/streams": "10.2.0",
"@verdaccio/tarball": "11.0.0-6-next.31",
"@verdaccio/ui-theme": "6.0.0-6-next.62",
"@verdaccio/url": "11.0.0-6-next.28",
"@verdaccio/utils": "6.0.0-6-next.30",
"@verdaccio/tarball": "11.0.0-6-next.32",
"@verdaccio/ui-theme": "6.0.0-6-next.63",
"@verdaccio/url": "11.0.0-6-next.29",
"@verdaccio/utils": "6.0.0-6-next.31",
"JSONStream": "1.3.5",
"async": "3.2.4",
"body-parser": "1.20.2",
Expand All @@ -55,7 +56,7 @@
"request": "2.88.2",
"semver": "7.3.8",
"validator": "13.9.0",
"verdaccio-audit": "11.0.0-6-next.23",
"verdaccio-audit": "11.0.0-6-next.26",
"verdaccio-htpasswd": "10.5.2"
},
"devDependencies": {
Expand Down Expand Up @@ -99,7 +100,7 @@
"@typescript-eslint/eslint-plugin": "5.49.0",
"@typescript-eslint/parser": "5.49.0",
"@verdaccio-scope/verdaccio-auth-foo": "0.0.2",
"@verdaccio/types": "10.7.0",
"@verdaccio/types": "10.7.1",
"babel-eslint": "10.1.0",
"babel-jest": "29.4.1",
"babel-plugin-dynamic-import-node": "2.3.3",
Expand Down Expand Up @@ -191,5 +192,10 @@
"url": "https://opencollective.com/verdaccio",
"logo": "https://opencollective.com/verdaccio/logo.txt"
},
"packageManager": "yarn@3.3.1"
"packageManager": "yarn@3.3.1",
"dependenciesMeta": {
"@verdaccio/logger-7@6.0.0-6-next.7": {
"unplugged": true
}
}
}
2 changes: 0 additions & 2 deletions src/api/web/api/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ function addPackageWebApi(storage: IStorageHandler, auth: IAuth, config: Config)
}

res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN);
const referer = req.get('Referer');
const pathname = referer ? new URL(referer).pathname : undefined;
next(parseReadme(info.name, info.readme));
},
});
Expand Down
8 changes: 1 addition & 7 deletions src/lib/auth-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import buildDebug from 'debug';
import _ from 'lodash';

import { aesDecryptDeprecated as aesDecrypt, verifyPayload } from '@verdaccio/signature';
import {
APITokenOptions,
Callback,
Expand Down Expand Up @@ -29,7 +30,6 @@ import {
TOKEN_BASIC,
TOKEN_BEARER,
} from './constants';
import { aesDecrypt, verifyPayload } from './crypto-utils';
import { logger } from './logger';
import { ErrorCode, convertPayloadToBase64 } from './utils';

Expand Down Expand Up @@ -156,12 +156,6 @@ const defaultApiTokenConf: APITokenOptions = {
legacy: true,
};

// we limit max 1000 request per 15 minutes on user endpoints
export const defaultUserRateLimiting = {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 1000,
};

export const defaultSecurity: Security = {
web: defaultWebTokenOptions,
api: defaultApiTokenConf,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import buildDebug from 'debug';
import { NextFunction } from 'express';
import _ from 'lodash';

import { aesEncryptDeprecated as aesEncrypt, signPayload } from '@verdaccio/signature';
import {
AllowAccess,
AuthPluginPackage,
Expand Down Expand Up @@ -31,7 +32,6 @@ import {
verifyJWTPayload,
} from './auth-utils';
import { API_ERROR, SUPPORT_ERRORS, TOKEN_BASIC, TOKEN_BEARER } from './constants';
import { aesEncrypt, signPayload } from './crypto-utils';
import { logger } from './logger';
import { ErrorCode, convertPayloadToBase64 } from './utils';

Expand Down
3 changes: 1 addition & 2 deletions src/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ if (process.getuid && process.getuid() === 0) {

// eslint-disable-next-line import/order
const logger = require('./logger');
logger.setup(null, { logStart: false }); // default setup

require('./cli/cli');

process.on('uncaughtException', function (err) {
logger.logger.fatal(
logger?.logger?.fatal(
{
err: err,
},
Expand Down
10 changes: 4 additions & 6 deletions src/lib/cli/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,14 @@ export class InitCommand extends Command {
let configPathLocation;
try {
configPathLocation = findConfigFile(this.config as string);
const verdaccioConfiguration = parseConfigFile(configPathLocation);
const verdaccioConfiguration: ReturnType<any> = parseConfigFile(configPathLocation);
if (!verdaccioConfiguration.self_path) {
verdaccioConfiguration.self_path = path.resolve(configPathLocation);
}
if (!verdaccioConfiguration.https) {
verdaccioConfiguration.https = { enable: false };
}

logger.logger.warn({ file: configPathLocation }, 'config file - @{file}');
process.title =
(verdaccioConfiguration.web && verdaccioConfiguration.web.title) || 'verdaccio';

Expand All @@ -73,11 +72,10 @@ export class InitCommand extends Command {
pkgName,
listenDefaultCallback
);
logger.logger.info({ file: configPathLocation }, 'config file - @{file}');
} catch (err) {
logger.logger.fatal(
{ file: configPathLocation, err: err },
'cannot open config file @{file}: @{!err.message}'
);
// eslint-disable-next-line no-console
console.error(`cannot open config file ${configPathLocation}: ${!err.message}`);
process.exit(1);
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/lib/config-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import _ from 'lodash';
import mkdirp from 'mkdirp';
import Path from 'path';

import { logger } from './logger';
import { fileExists, folderExists } from './utils';

const debug = buildDebug('verdaccio:config');
Expand Down Expand Up @@ -63,7 +62,6 @@ function readDefaultConfig(): string {

function createConfigFolder(configLocation): void {
mkdirp.sync(Path.dirname(configLocation.path));
logger.info({ file: configLocation.path }, 'Creating default config file in @{file}');
}

function updateStorageLinks(configLocation, defaultConfig): string {
Expand Down
33 changes: 0 additions & 33 deletions src/lib/config-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,6 @@ export function normalizeUserList(oldFormat: any, newFormat: any): any {
return _.flatten(result);
}

export function uplinkSanityCheck(
uplinks: UpLinksConfList,
users: any = BLACKLIST
): UpLinksConfList {
const newUplinks = _.clone(uplinks);
let newUsers = _.clone(users);

for (const uplink in newUplinks) {
if (Object.prototype.hasOwnProperty.call(newUplinks, uplink)) {
if (_.isNil(newUplinks[uplink].cache)) {
newUplinks[uplink].cache = true;
}
newUsers = sanityCheckNames(uplink, newUsers);
}
}

return newUplinks;
}

export function sanityCheckNames(item: string, users: any): any {
assert(
item !== 'all' &&
Expand All @@ -77,20 +58,6 @@ export function sanityCheckNames(item: string, users: any): any {
return users;
}

export function sanityCheckUplinksProps(configUpLinks: UpLinksConfList): UpLinksConfList {
const uplinks = _.clone(configUpLinks);

for (const uplink in uplinks) {
if (Object.prototype.hasOwnProperty.call(uplinks, uplink)) {
assert(uplinks[uplink].url, 'CONFIG: no url for uplink: ' + uplink);
assert(_.isString(uplinks[uplink].url), 'CONFIG: wrong url format for uplink: ' + uplink);
uplinks[uplink].url = uplinks[uplink].url.replace(/\/$/, '');
}
}

return uplinks;
}

/**
* Check whether an uplink can proxy
*/
Expand Down
113 changes: 6 additions & 107 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,113 +1,12 @@
import assert from 'assert';
// import assert from 'assert';
import _ from 'lodash';

import { getUserAgent } from '@verdaccio/config';
import { Config as AppConfig, Logger, PackageList, RateLimit, Security } from '@verdaccio/types';
import { generateRandomHexString, getMatchedPackagesSpec } from '@verdaccio/utils';
import { Config as ConfigCore } from '@verdaccio/config';

import { MatchedPackage, StartUpConfig } from '../types';
import { defaultUserRateLimiting } from './auth-utils';
import { normalisePackageAccess, sanityCheckUplinksProps, uplinkSanityCheck } from './config-utils';
import { APP_ERROR } from './constants';
import { isObject } from './utils';

const LoggerApi = require('./logger');
const strategicConfigProps = ['uplinks', 'packages'];
const allowedEnvConfig = ['http_proxy', 'https_proxy', 'no_proxy'];

/**
* Coordinates the application configuration
*/
class Config implements AppConfig {
public logger: Logger;
// @ts-ignore
public user_agent: boolean | string;
// @ts-ignore
public secret: string;
public uplinks: any;
public packages: PackageList;
public users: any;
public userRateLimit: RateLimit;
public server_id: string;
public self_path: string;
public storage: string | void;
public plugins: string | void;
// @ts-ignore
public security: Security;

public constructor(config: StartUpConfig) {
const self = this;
this.logger = LoggerApi.logger;
this.self_path = config.self_path;
this.storage = process.env.VERDACCIO_STORAGE_PATH || config.storage;
this.plugins = config.plugins;

for (const configProp in config) {
if (self[configProp] == null) {
self[configProp] = config[configProp];
}
}

if (config?.user_agent) {
this.user_agent = getUserAgent(config?.user_agent);
}

this.userRateLimit = { ...defaultUserRateLimiting, ...config?.userRateLimit };

// some weird shell scripts are valid yaml files parsed as string
assert(_.isObject(config), APP_ERROR.CONFIG_NOT_VALID);

// sanity check for strategic config properties
strategicConfigProps.forEach(function (x): void {
if (self[x] == null) {
self[x] = {};
}

assert(isObject(self[x]), `CONFIG: bad "${x}" value (object expected)`);
});

this.uplinks = sanityCheckUplinksProps(uplinkSanityCheck(this.uplinks));

if (_.isNil(this.users) === false) {
this.logger.warn(`[users]: property on configuration file
is not longer supported, property being ignored`);
}

this.packages = normalisePackageAccess(self.packages);

// loading these from ENV if aren't in config
allowedEnvConfig.forEach((envConf): void => {
if (!(envConf in self)) {
self[envConf] = process.env[envConf] || process.env[envConf.toUpperCase()];
}
});

// unique identifier of self server (or a cluster), used to avoid loops
// @ts-ignore
if (!this.server_id) {
this.server_id = generateRandomHexString(6);
}
}

/**
* Check for package spec
*/
public getMatchedPackagesSpec(pkgName: string): MatchedPackage {
return getMatchedPackagesSpec(pkgName, this.packages);
}

/**
* Store or create whether receive a secret key
*/
public checkSecretKey(secret: string): string {
if (_.isString(secret) && _.isEmpty(secret) === false) {
this.secret = secret;
return secret;
}
// it generates a secret key
// FUTURE: this might be an external secret key, perhaps within config file?
this.secret = generateRandomHexString(32);
return this.secret;
class Config extends ConfigCore {
public constructor(config: any) {
config.configPath = config.self_path;
super(config, { forceEnhancedLegacySignature: false });
}
}

Expand Down

0 comments on commit e50d4d9

Please sign in to comment.