diff --git a/.changeset/few-cooks-destroy.md b/.changeset/few-cooks-destroy.md new file mode 100644 index 000000000..2f6142373 --- /dev/null +++ b/.changeset/few-cooks-destroy.md @@ -0,0 +1,47 @@ +--- +'@verdaccio/api': major +'@verdaccio/auth': major +'@verdaccio/cli': major +'@verdaccio/dev-commons': major +'@verdaccio/config': major +'@verdaccio/commons-api': major +'@verdaccio/file-locking': major +'verdaccio-htpasswd': major +'@verdaccio/local-storage': major +'@verdaccio/readme': major +'@verdaccio/streams': major +'@verdaccio/types': major +'@verdaccio/hooks': major +'@verdaccio/loaders': major +'@verdaccio/logger': major +'@verdaccio/logger-prettify': major +'@verdaccio/middleware': major +'@verdaccio/mock': major +'@verdaccio/node-api': major +'@verdaccio/active-directory': major +'verdaccio-audit': major +'verdaccio-auth-memory': major +'verdaccio-aws-s3-storage': major +'verdaccio-google-cloud': major +'verdaccio-memory': major +'@verdaccio/proxy': major +'@verdaccio/server': major +'@verdaccio/store': major +'@verdaccio/dev-types': major +'@verdaccio/utils': major +'verdaccio': major +'@verdaccio/web': major +'@verdaccio/website': major +--- + +feat!: experiments config renamed to flags + +- The `experiments` configuration is renamed to `flags`. The functionality is exactly the same. + +```js +flags: token: false; +search: false; +``` + +- The `self_path` property from the config file is being removed in favor of `config_file` full path. +- Refactor `config` module, better types and utilities diff --git a/packages/auth/src/auth.ts b/packages/auth/src/auth.ts index 94e4aac97..eb84cfec3 100644 --- a/packages/auth/src/auth.ts +++ b/packages/auth/src/auth.ts @@ -35,7 +35,6 @@ import { getMatchedPackagesSpec } from '@verdaccio/config'; import { getMiddlewareCredentials, - getSecurity, getDefaultPlugins, verifyJWTPayload, parseAuthTokenHeader, @@ -292,6 +291,7 @@ class Auth implements IAuth { debug('allow unpublish for %o plugin does not implement allow_unpublish', packageName); continue; } else { + // @ts-ignore plugin.allow_unpublish!(user, pkg, (err, ok: boolean): void => { if (err) { debug( @@ -335,7 +335,7 @@ class Auth implements IAuth { (function next(): void { const plugin = plugins.shift(); - if (_.isNil(plugin) || isFunction(plugin.allow_publish) === false) { + if (isFunction(plugin?.allow_publish) === false) { debug('allow publish for %o plugin does not implement allow_publish', packageName); return next(); } @@ -343,6 +343,7 @@ class Auth implements IAuth { // @ts-ignore plugin.allow_publish( user, + // @ts-ignore pkg, // @ts-ignore (err: VerdaccioError, ok: boolean): void => { @@ -406,9 +407,7 @@ class Auth implements IAuth { debug('api middleware auth heather is not valid'); return next(getBadRequest(API_ERROR.BAD_AUTH_HEADER)); } - - const security: Security = getSecurity(this.config); - const { secret } = this.config; + const { secret, security } = this.config; if (isAESLegacy(security)) { debug('api middleware using legacy auth token'); @@ -554,6 +553,7 @@ class Auth implements IAuth { public async jwtEncrypt(user: RemoteUser, signOptions: JWTSignOptions): Promise { const { real_groups, name, groups } = user; + debug('jwt encrypt %o', name); const realGroupsValidated = _.isNil(real_groups) ? [] : real_groups; const groupedGroups = _.isNil(groups) ? real_groups : groups.concat(realGroupsValidated); const payload: RemoteUser = { diff --git a/packages/auth/src/utils.ts b/packages/auth/src/utils.ts index 3d4847290..6c1559aa9 100644 --- a/packages/auth/src/utils.ts +++ b/packages/auth/src/utils.ts @@ -1,16 +1,21 @@ import _ from 'lodash'; import buildDebug from 'debug'; -import { Callback, Config, IPluginAuth, RemoteUser, Security } from '@verdaccio/types'; +import { + Callback, + Config, + IPluginAuth, + RemoteUser, + Security, + AuthPackageAllow, +} from '@verdaccio/types'; import { HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER, API_ERROR } from '@verdaccio/dev-commons'; import { getForbidden, getUnauthorized, getConflict, getCode } from '@verdaccio/commons-api'; import { AllowAction, AllowActionCallback, - AuthPackageAllow, convertPayloadToBase64, createAnonymousRemoteUser, - defaultSecurity, } from '@verdaccio/utils'; import { TokenEncryption, AESPayload } from './auth'; import { aesDecrypt } from './legacy-token'; @@ -100,15 +105,16 @@ export async function getApiToken( remoteUser: RemoteUser, aesPassword: string ): Promise { - const security: Security = getSecurity(config); + debug('get api token'); + const { security } = config; if (isAESLegacy(security)) { + debug('security legacy enabled'); // fallback all goes to AES encryption return await new Promise((resolve): void => { resolve(auth.aesEncrypt(buildUser(remoteUser.name as string, aesPassword))); }); } - // i am wiling to use here _.isNil but flow does not like it yet. const { jwt } = security.api; if (jwt?.sign) { @@ -119,14 +125,6 @@ export async function getApiToken( }); } -export function getSecurity(config: Config): Security { - if (_.isNil(config.security) === false) { - return _.merge(defaultSecurity, config.security); - } - - return defaultSecurity; -} - export const expireReasons: string[] = ['JsonWebTokenError', 'TokenExpiredError']; export function verifyJWTPayload(token: string, secret: string): RemoteUser { diff --git a/packages/auth/test/auth-utils.spec.ts b/packages/auth/test/auth-utils.spec.ts index ef4b777cd..0154cecc5 100644 --- a/packages/auth/test/auth-utils.spec.ts +++ b/packages/auth/test/auth-utils.spec.ts @@ -26,7 +26,6 @@ import { getMiddlewareCredentials, getApiToken, verifyJWTPayload, - getSecurity, aesDecrypt, verifyPayload, signPayload, @@ -94,10 +93,8 @@ describe('Auth utilities', () => { }; const verifyAES = (token: string, user: string, password: string, secret: string) => { - const payload = aesDecrypt(token, secret).toString( - // @ts-ignore - CHARACTER_ENCODING.UTF8 - ); + // @ts-ignore + const payload = aesDecrypt(token, secret).toString(CHARACTER_ENCODING.UTF8); const content = payload.split(':'); expect(content[0]).toBe(user); @@ -339,7 +336,7 @@ describe('Auth utilities', () => { 'jwtEncrypt' ); const config: Config = getConfig('security-legacy', secret); - const security: Security = getSecurity(config); + const security: Security = config.security; const credentials = getMiddlewareCredentials(security, secret, `Bearer ${token}`); expect(credentials).toBeDefined(); // @ts-ignore @@ -355,7 +352,7 @@ describe('Auth utilities', () => { // basic authentication need send user as base64 const token = buildUserBuffer(user, pass).toString('base64'); const config: Config = getConfig('security-legacy', secret); - const security: Security = getSecurity(config); + const security: Security = config.security; const credentials = getMiddlewareCredentials(security, secret, `Basic ${token}`); expect(credentials).toBeDefined(); // @ts-ignore @@ -375,7 +372,7 @@ describe('Auth utilities', () => { 'jwtEncrypt' ); const config: Config = getConfig('security-legacy', secret); - const security: Security = getSecurity(config); + const security: Security = config.security; const credentials = getMiddlewareCredentials( security, 'b2df428b9929d3ace7c598bbf4e496_BAD_TOKEN', @@ -395,7 +392,7 @@ describe('Auth utilities', () => { 'jwtEncrypt' ); const config: Config = getConfig('security-legacy', secret); - const security: Security = getSecurity(config); + const security: Security = config.security; const credentials = getMiddlewareCredentials( security, secret, @@ -409,7 +406,7 @@ describe('Auth utilities', () => { const config: Config = getConfig('security-legacy', secret); const auth: IAuth = new Auth(config); const token = auth.aesEncrypt(null); - const security: Security = getSecurity(config); + const security: Security = config.security; const credentials = getMiddlewareCredentials( security, secret, @@ -438,7 +435,7 @@ describe('Auth utilities', () => { describe('should get JWT credentials', () => { test('should return anonymous whether token is corrupted', () => { const config: Config = getConfig('security-jwt', '12345'); - const security: Security = getSecurity(config); + const security: Security = config.security; const credentials = getMiddlewareCredentials( security, '12345', @@ -456,7 +453,7 @@ 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 security: Security = config.security; const credentials = getMiddlewareCredentials( security, '12345', @@ -478,7 +475,7 @@ describe('Auth utilities', () => { 'jwtEncrypt', 'aesEncrypt' ); - const security: Security = getSecurity(config); + const security: Security = config.security; const credentials = getMiddlewareCredentials( security, secret, diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index edd82f724..c82aec1e1 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -1,5 +1,4 @@ -import path from 'path'; - +import { ConfigRuntime } from '@verdaccio/types'; import { findConfigFile, parseConfigFile } from '@verdaccio/config'; import { startVerdaccio, listenDefaultCallback } from '@verdaccio/node-api'; @@ -10,22 +9,14 @@ export default function initProgram(commander, pkgVersion, pkgName) { // const initLogger = createLogger(); const cliListener = commander.listen; let configPathLocation; - let verdaccioConfiguration; + let verdaccioConfiguration: ConfigRuntime; try { configPathLocation = findConfigFile(commander.config); verdaccioConfiguration = parseConfigFile(configPathLocation); - const { web, https, self_path } = verdaccioConfiguration; + const { web, https } = verdaccioConfiguration; process.title = web?.title || DEFAULT_PROCESS_NAME; - // FIXME: self_path is only being used by @verdaccio/storage, not really useful - // and might be removed soon - if (!self_path) { - verdaccioConfiguration = Object.assign({}, verdaccioConfiguration, { - self_path: path.resolve(configPathLocation), - }); - } - if (!https) { verdaccioConfiguration = Object.assign({}, verdaccioConfiguration, { https: { enable: false }, diff --git a/packages/config/jest.config.js b/packages/config/jest.config.js index 7da7d2da8..1d530bfca 100644 --- a/packages/config/jest.config.js +++ b/packages/config/jest.config.js @@ -1,3 +1 @@ -const config = require('../../jest/config'); - -module.exports = Object.assign({}, config, {}); +module.exports = require('../../jest/config'); diff --git a/packages/config/src/agent.ts b/packages/config/src/agent.ts new file mode 100644 index 000000000..bf1f15d2c --- /dev/null +++ b/packages/config/src/agent.ts @@ -0,0 +1,11 @@ +import assert from 'assert'; +import _ from 'lodash'; + +const pkgVersion = require('../package.json').version; +const pkgName = require('../package.json').name; + +export function getUserAgent(): string { + assert(_.isString(pkgName)); + assert(_.isString(pkgVersion)); + return `${pkgName}/${pkgVersion}`; +} diff --git a/packages/config/src/conf/default.yaml b/packages/config/src/conf/default.yaml index e2f209e9c..9dced2110 100644 --- a/packages/config/src/conf/default.yaml +++ b/packages/config/src/conf/default.yaml @@ -87,12 +87,12 @@ logs: # { type: file, path: verdaccio.log, level: http} # FIXME: this should be documented # More info about log rotation https://github.com/pinojs/pino/blob/master/docs/help.md#log-rotation -#experiments: -# # support for npm token command -# token: false -# # support for the new v1 search endpoint, functional by incomplete read more on ticket 1732 -# search: false +flags: + # support for npm token command + token: false + # support for the new v1 search endpoint, functional by incomplete read more on ticket 1732 + search: false # This affect the web and api (not developed yet) -#i18n: -#web: en-US +i18n: + web: en-US diff --git a/packages/config/src/conf/docker.yaml b/packages/config/src/conf/docker.yaml index deb589904..a4c118028 100644 --- a/packages/config/src/conf/docker.yaml +++ b/packages/config/src/conf/docker.yaml @@ -86,12 +86,11 @@ logs: # FIXME: this should be documented # More info about log rotation https://github.com/pinojs/pino/blob/master/docs/help.md#log-rotation -#experiments: -# # support for npm token command -# token: false -# # support for the new v1 search endpoint, functional by incomplete read more on ticket 1732 -# search: false - +flags: + # support for npm token command + token: false + # support for the new v1 search endpoint, functional by incomplete read more on ticket 1732 + search: false # This affect the web and api (not developed yet) #i18n: #web: en-US diff --git a/packages/config/src/conf/full.yaml b/packages/config/src/conf/full.yaml deleted file mode 100644 index fd45178be..000000000 --- a/packages/config/src/conf/full.yaml +++ /dev/null @@ -1,10 +0,0 @@ -## This file is deprecated and the content does not exist anymore -## we highly recommend either visit -## https://verdaccio.org/docs/en/configuration -## or read the local file -## https://github.com/verdaccio/website/tree/master/docs/config.md - -## contribute with translations - -## You can contribute translating documentation through the crowdin platform -## https://crowdin.com/project/verdaccio diff --git a/packages/config/src/config-utils.ts b/packages/config/src/config-utils.ts deleted file mode 100644 index ed2c8b6bd..000000000 --- a/packages/config/src/config-utils.ts +++ /dev/null @@ -1,151 +0,0 @@ -import assert from 'assert'; -import _ from 'lodash'; -import minimatch from 'minimatch'; - -import { PackageList, UpLinksConfList, PackageAccess } from '@verdaccio/types'; -import { ErrorCode } from '@verdaccio/utils'; -import { MatchedPackage } from './config'; - -export type PackageAccessAddOn = PackageAccess & { - // FIXME: should be published on @verdaccio/types - unpublish?: string[]; -}; - -export interface LegacyPackageList { - [key: string]: PackageAccessAddOn; -} - -const BLACKLIST = { - all: true, - anonymous: true, - undefined: true, - owner: true, - none: true, -}; - -/** - * Normalize user list. - * @return {Array} - */ -export function normalizeUserList(groupsList: any): any { - const result: any[] = []; - if (_.isNil(groupsList)) { - return result; - } - - // if it's a string, split it to array - if (_.isString(groupsList)) { - const groupsArray = groupsList.split(/\s+/); - - result.push(groupsArray); - } else if (Array.isArray(groupsList)) { - result.push(groupsList); - } else { - throw ErrorCode.getInternalError( - 'CONFIG: bad package acl (array or string expected): ' + JSON.stringify(groupsList) - ); - } - - 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' && - item !== 'owner' && - item !== 'anonymous' && - item !== 'undefined' && - item !== 'none', - 'CONFIG: reserved uplink name: ' + item - ); - assert(!item.match(/\s/), 'CONFIG: invalid uplink name: ' + item); - assert(_.isNil(users[item]), 'CONFIG: duplicate uplink name: ' + item); - users[item] = true; - - 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 - */ -export function hasProxyTo(pkg: string, upLink: string, packages: PackageList): boolean { - const matchedPkg: MatchedPackage = getMatchedPackagesSpec(pkg, packages); - const proxyList = typeof matchedPkg !== 'undefined' ? matchedPkg.proxy : []; - if (proxyList) { - return proxyList.some((curr) => upLink === curr); - } - - return false; -} - -export function getMatchedPackagesSpec(pkgName: string, packages: PackageList): MatchedPackage { - for (const i in packages) { - if (minimatch.makeRe(i).exec(pkgName)) { - return packages[i]; - } - } - return; -} - -export function normalisePackageAccess(packages: LegacyPackageList): LegacyPackageList { - const normalizedPkgs: LegacyPackageList = { ...packages }; - // add a default rule for all packages to make writing plugins easier - if (_.isNil(normalizedPkgs['**'])) { - normalizedPkgs['**'] = { - access: [], - publish: [], - proxy: [], - }; - } - - for (const pkg in packages) { - if (Object.prototype.hasOwnProperty.call(packages, pkg)) { - const packageAccess = packages[pkg]; - const isInvalid = _.isObject(packageAccess) && _.isArray(packageAccess) === false; - assert(isInvalid, `CONFIG: bad "'${pkg}'" package description (object expected)`); - - normalizedPkgs[pkg].access = normalizeUserList(packageAccess.access); - normalizedPkgs[pkg].publish = normalizeUserList(packageAccess.publish); - normalizedPkgs[pkg].proxy = normalizeUserList(packageAccess.proxy); - // if unpublish is not defined, we set to false to fallback in publish access - normalizedPkgs[pkg].unpublish = _.isUndefined(packageAccess.unpublish) - ? false - : normalizeUserList(packageAccess.unpublish); - } - } - - return normalizedPkgs; -} diff --git a/packages/config/src/config.ts b/packages/config/src/config.ts index 57711cdb9..3e17fc756 100644 --- a/packages/config/src/config.ts +++ b/packages/config/src/config.ts @@ -2,28 +2,28 @@ import assert from 'assert'; import _ from 'lodash'; import buildDebug from 'debug'; -import { generateRandomHexString, getUserAgent, isObject } from '@verdaccio/utils'; +import { generateRandomHexString, isObject } from '@verdaccio/utils'; import { APP_ERROR } from '@verdaccio/dev-commons'; -import { PackageList, Config as AppConfig, Security, PackageAccess } from '@verdaccio/types'; -import { generateRandomSecretKey } from './token'; import { - getMatchedPackagesSpec, - normalisePackageAccess, - sanityCheckUplinksProps, - uplinkSanityCheck, -} from './config-utils'; + PackageList, + Config as AppConfig, + ConfigRuntime, + Security, + PackageAccess, + AuthConf, +} from '@verdaccio/types'; + +import { generateRandomSecretKey } from './token'; +import { getMatchedPackagesSpec, normalisePackageAccess } from './package-access'; +import { sanityCheckUplinksProps, uplinkSanityCheck } from './uplinks'; +import { defaultSecurity } from './security'; +import { getUserAgent } from './agent'; 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; -} - const debug = buildDebug('verdaccio:config'); /** @@ -31,23 +31,24 @@ const debug = buildDebug('verdaccio:config'); */ class Config implements AppConfig { public user_agent: string; - // @ts-ignore - public secret: string; public uplinks: any; public packages: PackageList; public users: any; + public auth: AuthConf; public server_id: string; - public self_path: string; + public config_path: string; public storage: string | void; public plugins: string | void; - // @ts-ignore public security: Security; + // @ts-ignore + public secret: string; - public constructor(config: StartUpConfig) { + public constructor(config: ConfigRuntime) { const self = this; - this.self_path = config.self_path; this.storage = config.storage; + this.config_path = config.config_path; this.plugins = config.plugins; + this.security = _.merge(defaultSecurity, config.security); for (const configProp in config) { if (self[configProp] == null) { diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index 0f6350128..2d370a082 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -1,5 +1,7 @@ export * from './config'; export * from './config-path'; export * from './token'; -export * from './config-utils'; +export * from './package-access'; export * from './parse'; +export * from './uplinks'; +export * from './security'; diff --git a/packages/config/src/package-access.ts b/packages/config/src/package-access.ts new file mode 100644 index 000000000..661c6e5a3 --- /dev/null +++ b/packages/config/src/package-access.ts @@ -0,0 +1,72 @@ +import assert from 'assert'; +import _ from 'lodash'; +import minimatch from 'minimatch'; + +import { PackageList, PackageAccess } from '@verdaccio/types'; +import { ErrorCode } from '@verdaccio/utils'; +import { MatchedPackage } from './config'; + +export interface LegacyPackageList { + [key: string]: PackageAccess; +} + +export function normalizeUserList(groupsList: any): any { + const result: any[] = []; + if (_.isNil(groupsList)) { + return result; + } + + // if it's a string, split it to array + if (_.isString(groupsList)) { + const groupsArray = groupsList.split(/\s+/); + + result.push(groupsArray); + } else if (Array.isArray(groupsList)) { + result.push(groupsList); + } else { + throw ErrorCode.getInternalError( + 'CONFIG: bad package acl (array or string expected): ' + JSON.stringify(groupsList) + ); + } + + return _.flatten(result); +} + +export function getMatchedPackagesSpec(pkgName: string, packages: PackageList): MatchedPackage { + for (const i in packages) { + if (minimatch.makeRe(i).exec(pkgName)) { + return packages[i]; + } + } + return; +} + +export function normalisePackageAccess(packages: LegacyPackageList): LegacyPackageList { + const normalizedPkgs: LegacyPackageList = { ...packages }; + if (_.isNil(normalizedPkgs['**'])) { + normalizedPkgs['**'] = { + access: [], + publish: [], + unpublish: [], + proxy: [], + }; + } + + for (const pkg in packages) { + if (Object.prototype.hasOwnProperty.call(packages, pkg)) { + const packageAccess = packages[pkg]; + const isInvalid = _.isObject(packageAccess) && _.isArray(packageAccess) === false; + assert(isInvalid, `CONFIG: bad "'${pkg}'" package description (object expected)`); + + normalizedPkgs[pkg].access = normalizeUserList(packageAccess.access); + normalizedPkgs[pkg].publish = normalizeUserList(packageAccess.publish); + normalizedPkgs[pkg].proxy = normalizeUserList(packageAccess.proxy); + // if unpublish is not defined, we set to false to fallback in publish access + normalizedPkgs[pkg].unpublish = _.isUndefined(packageAccess.unpublish) + ? false + : normalizeUserList(packageAccess.unpublish); + } + } + + return normalizedPkgs; +} diff --git a/packages/config/src/parse.ts b/packages/config/src/parse.ts index 639e10eeb..28a7dc57c 100644 --- a/packages/config/src/parse.ts +++ b/packages/config/src/parse.ts @@ -1,14 +1,21 @@ import fs from 'fs'; import YAML from 'js-yaml'; -import { APP_ERROR, CHARACTER_ENCODING } from '@verdaccio/dev-commons'; +import { APP_ERROR } from '@verdaccio/dev-commons'; +import { ConfigRuntime, ConfigYaml } from '@verdaccio/types'; -export function parseConfigFile(configPath: string): any { +export function parseConfigFile(configPath: string): ConfigRuntime { try { if (/\.ya?ml$/i.test(configPath)) { - // @ts-ignore - return YAML.safeLoad(fs.readFileSync(configPath, CHARACTER_ENCODING.UTF8)); + const yamlConfig = YAML.safeLoad(fs.readFileSync(configPath, 'utf8')) as ConfigYaml; + return Object.assign({}, yamlConfig, { + config_path: configPath, + }); } - return require(configPath); + + const jsonConfig = require(configPath) as ConfigYaml; + return Object.assign({}, jsonConfig, { + config_path: configPath, + }); } catch (e) { if (e.code !== 'MODULE_NOT_FOUND') { e.message = APP_ERROR.CONFIG_NOT_VALID; diff --git a/packages/config/src/security.ts b/packages/config/src/security.ts new file mode 100644 index 000000000..9849aa67e --- /dev/null +++ b/packages/config/src/security.ts @@ -0,0 +1,20 @@ +import { APITokenOptions, JWTOptions, Security } from '@verdaccio/types'; + +export const TIME_EXPIRATION_7D = '7d'; + +const defaultWebTokenOptions: JWTOptions = { + sign: { + // The expiration token for the website is 7 days + expiresIn: TIME_EXPIRATION_7D, + }, + verify: {}, +}; + +const defaultApiTokenConf: APITokenOptions = { + legacy: true, +}; + +export const defaultSecurity: Security = { + web: defaultWebTokenOptions, + api: defaultApiTokenConf, +}; diff --git a/packages/utils/src/string.ts b/packages/config/src/string.ts similarity index 100% rename from packages/utils/src/string.ts rename to packages/config/src/string.ts diff --git a/packages/config/src/uplinks.ts b/packages/config/src/uplinks.ts new file mode 100644 index 000000000..bc67e8364 --- /dev/null +++ b/packages/config/src/uplinks.ts @@ -0,0 +1,76 @@ +import assert from 'assert'; +import { PackageList, UpLinksConfList } from '@verdaccio/types'; +import _ from 'lodash'; + +import { getMatchedPackagesSpec } from './package-access'; +import { MatchedPackage } from './config'; + +const BLACKLIST = { + all: true, + anonymous: true, + undefined: true, + owner: true, + none: true, +}; + +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 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 + */ +export function hasProxyTo(pkg: string, upLink: string, packages: PackageList): boolean { + const matchedPkg: MatchedPackage = getMatchedPackagesSpec(pkg, packages); + const proxyList = typeof matchedPkg !== 'undefined' ? matchedPkg.proxy : []; + if (proxyList) { + return proxyList.some((curr) => upLink === curr); + } + + return false; +} + +export function sanityCheckNames(item: string, users: any): any { + assert( + item !== 'all' && + item !== 'owner' && + item !== 'anonymous' && + item !== 'undefined' && + item !== 'none', + 'CONFIG: reserved uplink name: ' + item + ); + assert(!item.match(/\s/), 'CONFIG: invalid uplink name: ' + item); + assert(_.isNil(users[item]), 'CONFIG: duplicate uplink name: ' + item); + users[item] = true; + + return users; +} diff --git a/packages/config/test/config-parsing.spec.ts b/packages/config/test/config-parsing.spec.ts new file mode 100644 index 000000000..e317c8a51 --- /dev/null +++ b/packages/config/test/config-parsing.spec.ts @@ -0,0 +1,51 @@ +import path from 'path'; +import { parseConfigFile } from '../src'; + +describe('Package access utilities', () => { + const parseConfigurationFile = (conf) => { + const { name, ext } = path.parse(conf); + const format = ext.startsWith('.') ? ext.substring(1) : 'yaml'; + + return path.join(__dirname, `./partials/config/${format}/${name}.${format}`); + }; + + describe('JSON format', () => { + test('parse default.json', () => { + const config = parseConfigFile(parseConfigurationFile('default.json')); + + expect(config.storage).toBeDefined(); + }); + + test('parse invalid.json', () => { + expect(function () { + parseConfigFile(parseConfigurationFile('invalid.json')); + }).toThrow(/Error/); + }); + + test('parse not-exists.json', () => { + expect(function () { + parseConfigFile(parseConfigurationFile('not-exists.json')); + }).toThrow(/Error/); + }); + }); + + describe('JavaScript format', () => { + test('parse default.js', () => { + const config = parseConfigFile(parseConfigurationFile('default.js')); + + expect(config.storage).toBeDefined(); + }); + + test('parse invalid.js', () => { + expect(function () { + parseConfigFile(parseConfigurationFile('invalid.js')); + }).toThrow(/Error/); + }); + + test('parse not-exists.js', () => { + expect(function () { + parseConfigFile(parseConfigurationFile('not-exists.js')); + }).toThrow(/Error/); + }); + }); +}); diff --git a/packages/config/test/config.spec.ts b/packages/config/test/config.spec.ts index c3bd3bc68..5061c3c62 100644 --- a/packages/config/test/config.spec.ts +++ b/packages/config/test/config.spec.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; import { DEFAULT_REGISTRY, DEFAULT_UPLINK, ROLES, WEB_TITLE } from '@verdaccio/dev-commons'; -import { Config, parseConfigFile } from '../src'; +import { Config, defaultSecurity, parseConfigFile } from '../src'; const resolveConf = (conf) => { const { name, ext } = path.parse(conf); @@ -62,33 +62,23 @@ const checkDefaultConfPackages = (config) => { expect(config.url_prefix).toBeUndefined(); expect(config.experiments).toBeUndefined(); - expect(config.security).toBeUndefined(); + expect(config.security).toEqual(defaultSecurity); }; -describe('Config file', () => { - beforeAll(function () { - /* eslint no-invalid-this: 0 */ - // @ts-ignore - this.config = new Config(parseConfigFile(resolveConf('default'))); +describe('check basic content parsed file', () => { + test('parse default.yaml', () => { + const config = new Config(parseConfigFile(resolveConf('default'))); + checkDefaultUplink(config); + expect(config.storage).toBe('./storage'); + expect(config.auth.htpasswd.file).toBe('./htpasswd'); + checkDefaultConfPackages(config); }); - describe('Config file', () => { - test('parse docker.yaml', () => { - const config = new Config(parseConfigFile(resolveConf('docker'))); - checkDefaultUplink(config); - expect(config.storage).toBe('/verdaccio/storage/data'); - // @ts-ignore - expect(config.auth.htpasswd.file).toBe('/verdaccio/storage/htpasswd'); - checkDefaultConfPackages(config); - }); - - test('parse default.yaml', () => { - const config = new Config(parseConfigFile(resolveConf('default'))); - checkDefaultUplink(config); - expect(config.storage).toBe('./storage'); - // @ts-ignore - expect(config.auth.htpasswd.file).toBe('./htpasswd'); - checkDefaultConfPackages(config); - }); + test('parse docker.yaml', () => { + const config = new Config(parseConfigFile(resolveConf('docker'))); + checkDefaultUplink(config); + expect(config.storage).toBe('/verdaccio/storage/data'); + expect(config.auth.htpasswd.file).toBe('/verdaccio/storage/htpasswd'); + checkDefaultConfPackages(config); }); }); diff --git a/packages/config/test/config-utils.spec.ts b/packages/config/test/package-access.spec.ts similarity index 55% rename from packages/config/test/config-utils.spec.ts rename to packages/config/test/package-access.spec.ts index e4b9413eb..d4a97a94d 100644 --- a/packages/config/test/config-utils.spec.ts +++ b/packages/config/test/package-access.spec.ts @@ -3,17 +3,10 @@ import _ from 'lodash'; import { PACKAGE_ACCESS } from '@verdaccio/dev-commons'; -import { spliceURL } from '@verdaccio/utils'; -import { - getMatchedPackagesSpec, - hasProxyTo, - normalisePackageAccess, - sanityCheckUplinksProps, - uplinkSanityCheck, -} from '../src/config-utils'; +import { getMatchedPackagesSpec, normalisePackageAccess } from '../src/package-access'; import { parseConfigFile } from '../src'; -describe('Config Utilities', () => { +describe('Package access utilities', () => { const parseConfigurationFile = (conf) => { const { name, ext } = path.parse(conf); const format = ext.startsWith('.') ? ext.substring(1) : 'yaml'; @@ -21,38 +14,6 @@ describe('Config Utilities', () => { return path.join(__dirname, `./partials/config/${format}/${name}.${format}`); }; - describe('uplinkSanityCheck', () => { - test('should test basic conversion', () => { - const uplinks = uplinkSanityCheck( - parseConfigFile(parseConfigurationFile('uplink-basic')).uplinks - ); - expect(Object.keys(uplinks)).toContain('server1'); - expect(Object.keys(uplinks)).toContain('server2'); - }); - - test('should throw error on blacklisted uplink name', () => { - const { uplinks } = parseConfigFile(parseConfigurationFile('uplink-wrong')); - - expect(() => { - uplinkSanityCheck(uplinks); - }).toThrow('CONFIG: reserved uplink name: anonymous'); - }); - }); - - describe('sanityCheckUplinksProps', () => { - test('should fails if url prop is missing', () => { - const { uplinks } = parseConfigFile(parseConfigurationFile('uplink-wrong')); - expect(() => { - sanityCheckUplinksProps(uplinks); - }).toThrow('CONFIG: no url for uplink: none-url'); - }); - - test('should bypass an empty uplink list', () => { - // @ts-ignore - expect(sanityCheckUplinksProps([])).toHaveLength(0); - }); - }); - describe('normalisePackageAccess', () => { test('should test basic conversion', () => { const { packages } = parseConfigFile(parseConfigurationFile('pkgs-basic')); @@ -207,113 +168,4 @@ describe('Config Utilities', () => { expect(getMatchedPackagesSpec('@scope/vue', packages)).toBeUndefined(); }); }); - - describe('hasProxyTo', () => { - test('should test basic config', () => { - const packages = normalisePackageAccess( - parseConfigFile(parseConfigurationFile('pkgs-basic')).packages - ); - // react - expect(hasProxyTo('react', 'facebook', packages)).toBeFalsy(); - expect(hasProxyTo('react', 'google', packages)).toBeFalsy(); - // vue - expect(hasProxyTo('vue', 'google', packages)).toBeFalsy(); - expect(hasProxyTo('vue', 'fake', packages)).toBeFalsy(); - expect(hasProxyTo('vue', 'npmjs', packages)).toBeTruthy(); - // angular - expect(hasProxyTo('angular', 'google', packages)).toBeFalsy(); - expect(hasProxyTo('angular', 'facebook', packages)).toBeFalsy(); - expect(hasProxyTo('angular', 'npmjs', packages)).toBeTruthy(); - }); - - test('should test resolve based on custom package access', () => { - const packages = normalisePackageAccess( - parseConfigFile(parseConfigurationFile('pkgs-custom')).packages - ); - // react - expect(hasProxyTo('react', 'facebook', packages)).toBeTruthy(); - expect(hasProxyTo('react', 'google', packages)).toBeFalsy(); - // vue - expect(hasProxyTo('vue', 'google', packages)).toBeFalsy(); - expect(hasProxyTo('vue', 'fake', packages)).toBeFalsy(); - expect(hasProxyTo('vue', 'npmjs', packages)).toBeTruthy(); - // angular - expect(hasProxyTo('angular', 'google', packages)).toBeTruthy(); - expect(hasProxyTo('angular', 'facebook', packages)).toBeFalsy(); - expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); - }); - - test('should not resolve any proxy', () => { - const packages = normalisePackageAccess( - parseConfigFile(parseConfigurationFile('pkgs-empty')).packages - ); - // react - expect(hasProxyTo('react', 'npmjs', packages)).toBeFalsy(); - expect(hasProxyTo('react', 'npmjs', packages)).toBeFalsy(); - // vue - expect(hasProxyTo('vue', 'npmjs', packages)).toBeFalsy(); - expect(hasProxyTo('vue', 'npmjs', packages)).toBeFalsy(); - expect(hasProxyTo('vue', 'npmjs', packages)).toBeFalsy(); - // angular - expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); - expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); - expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); - // private - expect(hasProxyTo('private', 'fake', packages)).toBeFalsy(); - }); - }); - - describe('spliceURL', () => { - test('should splice two strings and generate a url', () => { - const url: string = spliceURL('http://domain.com', '/-/static/logo.png'); - - expect(url).toMatch('http://domain.com/-/static/logo.png'); - }); - - test('should splice a empty strings and generate a url', () => { - const url: string = spliceURL('', '/-/static/logo.png'); - - expect(url).toMatch('/-/static/logo.png'); - }); - }); - - describe('JSON', () => { - test('parse default.json', () => { - const config = parseConfigFile(parseConfigurationFile('default.json')); - - expect(config.storage).toBeDefined(); - }); - - test('parse invalid.json', () => { - expect(function () { - parseConfigFile(parseConfigurationFile('invalid.json')); - }).toThrow(/Error/); - }); - - test('parse not-exists.json', () => { - expect(function () { - parseConfigFile(parseConfigurationFile('not-exists.json')); - }).toThrow(/Error/); - }); - }); - - describe('JavaScript', () => { - test('parse default.js', () => { - const config = parseConfigFile(parseConfigurationFile('default.js')); - - expect(config.storage).toBeDefined(); - }); - - test('parse invalid.js', () => { - expect(function () { - parseConfigFile(parseConfigurationFile('invalid.js')); - }).toThrow(/Error/); - }); - - test('parse not-exists.js', () => { - expect(function () { - parseConfigFile(parseConfigurationFile('not-exists.js')); - }).toThrow(/Error/); - }); - }); }); diff --git a/packages/config/test/uplinks.spec.ts b/packages/config/test/uplinks.spec.ts new file mode 100644 index 000000000..44525d034 --- /dev/null +++ b/packages/config/test/uplinks.spec.ts @@ -0,0 +1,100 @@ +import path from 'path'; + +import { hasProxyTo, sanityCheckUplinksProps, uplinkSanityCheck } from '../src/uplinks'; +import { normalisePackageAccess, parseConfigFile } from '../src'; + +describe('Uplinks Utilities', () => { + const parseConfigurationFile = (conf) => { + const { name, ext } = path.parse(conf); + const format = ext.startsWith('.') ? ext.substring(1) : 'yaml'; + + return path.join(__dirname, `./partials/config/${format}/${name}.${format}`); + }; + + describe('uplinkSanityCheck', () => { + test('should test basic conversion', () => { + const uplinks = uplinkSanityCheck( + parseConfigFile(parseConfigurationFile('uplink-basic')).uplinks + ); + expect(Object.keys(uplinks)).toContain('server1'); + expect(Object.keys(uplinks)).toContain('server2'); + }); + + test('should throw error on blacklisted uplink name', () => { + const { uplinks } = parseConfigFile(parseConfigurationFile('uplink-wrong')); + + expect(() => { + uplinkSanityCheck(uplinks); + }).toThrow('CONFIG: reserved uplink name: anonymous'); + }); + }); + + describe('sanityCheckUplinksProps', () => { + test('should fails if url prop is missing', () => { + const { uplinks } = parseConfigFile(parseConfigurationFile('uplink-wrong')); + expect(() => { + sanityCheckUplinksProps(uplinks); + }).toThrow('CONFIG: no url for uplink: none-url'); + }); + + test('should bypass an empty uplink list', () => { + // @ts-ignore + expect(sanityCheckUplinksProps([])).toHaveLength(0); + }); + }); + + describe('hasProxyTo', () => { + test('should test basic config', () => { + const packages = normalisePackageAccess( + parseConfigFile(parseConfigurationFile('pkgs-basic')).packages + ); + // react + expect(hasProxyTo('react', 'facebook', packages)).toBeFalsy(); + expect(hasProxyTo('react', 'google', packages)).toBeFalsy(); + // vue + expect(hasProxyTo('vue', 'google', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'fake', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'npmjs', packages)).toBeTruthy(); + // angular + expect(hasProxyTo('angular', 'google', packages)).toBeFalsy(); + expect(hasProxyTo('angular', 'facebook', packages)).toBeFalsy(); + expect(hasProxyTo('angular', 'npmjs', packages)).toBeTruthy(); + }); + + test('should test resolve based on custom package access', () => { + const packages = normalisePackageAccess( + parseConfigFile(parseConfigurationFile('pkgs-custom')).packages + ); + // react + expect(hasProxyTo('react', 'facebook', packages)).toBeTruthy(); + expect(hasProxyTo('react', 'google', packages)).toBeFalsy(); + // vue + expect(hasProxyTo('vue', 'google', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'fake', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'npmjs', packages)).toBeTruthy(); + // angular + expect(hasProxyTo('angular', 'google', packages)).toBeTruthy(); + expect(hasProxyTo('angular', 'facebook', packages)).toBeFalsy(); + expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); + }); + + test('should not resolve any proxy', () => { + const packages = normalisePackageAccess( + parseConfigFile(parseConfigurationFile('pkgs-empty')).packages + ); + // react + expect(hasProxyTo('react', 'npmjs', packages)).toBeFalsy(); + expect(hasProxyTo('react', 'npmjs', packages)).toBeFalsy(); + // vue + expect(hasProxyTo('vue', 'npmjs', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'npmjs', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'npmjs', packages)).toBeFalsy(); + // angular + expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); + expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); + expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); + // private + expect(hasProxyTo('private', 'fake', packages)).toBeFalsy(); + }); + }); +}); diff --git a/packages/config/test/utils.spec.ts b/packages/config/test/utils.spec.ts new file mode 100644 index 000000000..ba300820b --- /dev/null +++ b/packages/config/test/utils.spec.ts @@ -0,0 +1,15 @@ +import { spliceURL } from '../src/string'; + +describe('spliceURL', () => { + test('should splice two strings and generate a url', () => { + const url: string = spliceURL('http://domain.com', '/-/static/logo.png'); + + expect(url).toMatch('http://domain.com/-/static/logo.png'); + }); + + test('should splice a empty strings and generate a url', () => { + const url: string = spliceURL('', '/-/static/logo.png'); + + expect(url).toMatch('/-/static/logo.png'); + }); +}); diff --git a/packages/core/htpasswd/htpasswd b/packages/core/htpasswd/htpasswd new file mode 100644 index 000000000..ee06bb25d --- /dev/null +++ b/packages/core/htpasswd/htpasswd @@ -0,0 +1 @@ +test:$6xJ4qsJ2xHE2:autocreated 2020-11-08T10:29:19.160Z diff --git a/packages/core/htpasswd/src/htpasswd.ts b/packages/core/htpasswd/src/htpasswd.ts index 5e90409a0..ae182f87a 100644 --- a/packages/core/htpasswd/src/htpasswd.ts +++ b/packages/core/htpasswd/src/htpasswd.ts @@ -59,7 +59,7 @@ export default class HTPasswd implements IPluginAuth { throw new Error('should specify "file" in config'); } - this.path = Path.resolve(Path.dirname(this.verdaccioConfig.self_path), file); + this.path = Path.resolve(Path.dirname(this.verdaccioConfig.config_path), file); } /** diff --git a/packages/core/htpasswd/tests/__mocks__/Config.js b/packages/core/htpasswd/tests/__mocks__/Config.js index 507edb36a..3dfc3a124 100644 --- a/packages/core/htpasswd/tests/__mocks__/Config.js +++ b/packages/core/htpasswd/tests/__mocks__/Config.js @@ -38,7 +38,7 @@ export default class Config { level: 35, }, ]; - this.self_path = './tests/__fixtures__/config.yaml'; + this.config_path = './tests/__fixtures__/config.yaml'; this.https = { enable: false, }; diff --git a/packages/core/local-storage/src/local-database.ts b/packages/core/local-storage/src/local-database.ts index c4412e05b..5b987972a 100644 --- a/packages/core/local-storage/src/local-database.ts +++ b/packages/core/local-storage/src/local-database.ts @@ -99,7 +99,7 @@ class LocalDatabase implements IPluginStorage<{}> { ): void { const storages = this._getCustomPackageLocalStorages(); debug(`search custom local packages: %o`, JSON.stringify(storages)); - const base = Path.dirname(this.config.self_path); + const base = Path.dirname(this.config.config_path); const self = this; const storageKeys = Object.keys(storages); debug(`search base: %o keys: %o`, base, storageKeys); @@ -241,7 +241,7 @@ class LocalDatabase implements IPluginStorage<{}> { } const packageStoragePath: string = Path.join( - Path.resolve(Path.dirname(this.config.self_path || ''), packagePath), + Path.resolve(Path.dirname(this.config.config_path || ''), packagePath), packageName ); @@ -412,7 +412,7 @@ 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) + Path.resolve(Path.dirname(config.config_path || ''), config.storage as string, dbName) ); } diff --git a/packages/core/local-storage/tests/__mocks__/Config.ts b/packages/core/local-storage/tests/__mocks__/Config.ts index 7db13d9c9..efafda437 100644 --- a/packages/core/local-storage/tests/__mocks__/Config.ts +++ b/packages/core/local-storage/tests/__mocks__/Config.ts @@ -61,7 +61,7 @@ export default class Config { }, ]; - this.self_path = './tests/__fixtures__/config.yaml'; + this.config_path = './tests/__fixtures__/config.yaml'; this.https = { enable: false, diff --git a/packages/core/types/index.d.ts b/packages/core/types/index.d.ts index 931b28206..6c045bd35 100644 --- a/packages/core/types/index.d.ts +++ b/packages/core/types/index.d.ts @@ -187,8 +187,18 @@ declare module '@verdaccio/types' { publish?: string[]; proxy?: string[]; access?: string[]; + unpublish: string[]; } + // info passed to the auth plugin when a package is package is being published + interface AllowAccess { + name: string; + version?: string; + tag?: string; + } + + interface AuthPackageAllow extends PackageAccess, AllowAccess {} + interface PackageList { [key: string]: PackageAccess; } @@ -323,14 +333,14 @@ declare module '@verdaccio/types' { api: APITokenOptions; } - interface Config { - user_agent: string; - server_id: any; + interface ConfigFlags { + token?: boolean; + search?: boolean; + } + + interface ConfigYaml { _debug?: boolean; storage?: string | void; - plugins?: string | void; - secret: string; - self_path: string; packages: PackageList; uplinks: UpLinksConfList; logs?: LoggerConf[]; @@ -338,19 +348,31 @@ declare module '@verdaccio/types' { auth?: AuthConf; security: Security; publish?: PublishOptions; - url_prefix?: string; store?: any; listen?: ListenAddress; https?: HttpsConf; http_proxy?: string; + plugins?: string | void; https_proxy?: string; no_proxy?: string; max_body_size?: string; - // deprecated notifications?: Notifications; notify?: Notifications | Notifications[]; middlewares?: any; filters?: any; + url_prefix?: string; + flags?: ConfigFlags; + } + + interface ConfigRuntime extends ConfigYaml { + config_path: string; + } + + interface Config extends ConfigYaml, ConfigRuntime { + user_agent: string; + server_id: string; + secret: string; + // deprecated checkSecretKey(token: string): string; getMatchedPackagesSpec(storage: string): PackageAccess | void; [key: string]: any; @@ -487,12 +509,6 @@ declare module '@verdaccio/types' { logger: Logger; } - interface AllowAccess { - name: string; - version?: string; - tag?: string; - } - // FIXME: error should be export type `VerdaccioError = HttpError & { code: number };` // instead of AuthError // but this type is on @verdaccio/commons-api and cannot be used here yet (I don't know why) @@ -514,9 +530,9 @@ declare module '@verdaccio/types' { authenticate(user: string, password: string, cb: AuthCallback): void; adduser?(user: string, password: string, cb: AuthCallback): void; changePassword?(user: string, password: string, newPassword: string, cb: AuthCallback): void; - allow_publish?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void; + allow_publish?(user: RemoteUser, pkg: T & AuthPackageAllow, cb: AuthAccessCallback): void; allow_access?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void; - allow_unpublish?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void; + allow_unpublish?(user: RemoteUser, pkg: T & AuthPackageAllow, cb: AuthAccessCallback): void; allow_publish?( user: RemoteUser, pkg: AllowAccess & PackageAccess, diff --git a/packages/loaders/src/plugin-loader.ts b/packages/loaders/src/plugin-loader.ts index 62c89ac1f..d6b07c44e 100644 --- a/packages/loaders/src/plugin-loader.ts +++ b/packages/loaders/src/plugin-loader.ts @@ -97,7 +97,7 @@ export function loadPlugin>( // relative to config path if (plugin === null && pluginId.match(/^\.\.?($|\/)/)) { - plugin = tryLoad(Path.resolve(Path.dirname(config.self_path), pluginId)); + plugin = tryLoad(Path.resolve(Path.dirname(config.config_path), pluginId)); } if (plugin === null) { diff --git a/packages/loaders/test/plugin_loader.spec.ts b/packages/loaders/test/plugin_loader.spec.ts index 587f8a654..c44e131bc 100644 --- a/packages/loaders/test/plugin_loader.spec.ts +++ b/packages/loaders/test/plugin_loader.spec.ts @@ -9,7 +9,7 @@ describe('plugin loader', () => { const relativePath = path.join(__dirname, './partials/test-plugin-storage'); const buildConf = (name) => { return { - self_path: path.join(__dirname, './'), + config_path: path.join(__dirname, './'), max_users: 0, auth: { [`${relativePath}/${name}`]: {}, diff --git a/packages/mock/src/mock.ts b/packages/mock/src/mock.ts index f8160879e..e9d65adee 100644 --- a/packages/mock/src/mock.ts +++ b/packages/mock/src/mock.ts @@ -32,7 +32,7 @@ import { IServerBridge } from './types'; file: './test-profile-storage/.htpasswd' } }, - self_path: store + config_path: store }); app = await endPointAPI(configForTest); mockRegistry = await mockServer(mockServerPort).init(); diff --git a/packages/node-api/package.json b/packages/node-api/package.json index 718c5429d..fa103cb08 100644 --- a/packages/node-api/package.json +++ b/packages/node-api/package.json @@ -29,6 +29,7 @@ "@verdaccio/config": "workspace:5.0.0-alpha.0", "@verdaccio/utils": "workspace:5.0.0-alpha.0", "lodash": "^4.17.20", + "debug": "^4.2.0", "core-js": "^3.6.5", "selfsigned": "1.10.7" }, diff --git a/packages/node-api/src/bootstrap.ts b/packages/node-api/src/bootstrap.ts index ed06e287d..50828e078 100644 --- a/packages/node-api/src/bootstrap.ts +++ b/packages/node-api/src/bootstrap.ts @@ -5,8 +5,15 @@ import https from 'https'; import constants from 'constants'; import { Application } from 'express'; import { assign, isObject, isFunction } from 'lodash'; +import buildDebug from 'debug'; -import { Callback, ConfigWithHttps, HttpsConfKeyCert, HttpsConfPfx } from '@verdaccio/types'; +import { + ConfigRuntime, + Callback, + ConfigWithHttps, + HttpsConfKeyCert, + HttpsConfPfx, +} from '@verdaccio/types'; import { API_ERROR, certPem, csrPem, keyPem } from '@verdaccio/dev-commons'; import server from '@verdaccio/server'; import { logger } from '@verdaccio/logger'; @@ -14,6 +21,8 @@ import { logger } from '@verdaccio/logger'; import { getListListenAddresses, resolveConfigPath } from './cli-utils'; import { displayExperimentsInfoBox } from './experiments'; +const debug = buildDebug('verdaccio:runtime'); + function launchServer( app, addr, @@ -25,9 +34,11 @@ function launchServer( ): void { let webServer; if (addr.proto === 'https') { + debug('https enabled'); webServer = handleHTTPS(app, configPath, config); } else { // http + debug('http enabled'); webServer = http.createServer(app); } if ( @@ -43,36 +54,25 @@ function launchServer( callback(webServer, addr, pkgName, pkgVersion); } -/** - * Trigger the server after configuration has been loaded. - * @param {Object} config - * @param {Object} cliArguments - * @param {String} configPath - * @param {String} pkgVersion - * @param {String} pkgName - */ -function startVerdaccio( - config: any, +async function startVerdaccio( + config: ConfigRuntime, cliListen: string, configPath: string, pkgVersion: string, pkgName: string, callback: Callback -): void { +): Promise { if (isObject(config) === false) { throw new Error(API_ERROR.CONFIG_BAD_FORMAT); } - server(config).then((app): void => { - const addresses = getListListenAddresses(cliListen, config.listen); - if ('experiments' in config) { - displayExperimentsInfoBox(config.experiments); - } + const app = await server(config); + const addresses = getListListenAddresses(cliListen, config.listen); + displayExperimentsInfoBox(config.flags); - addresses.forEach((addr) => - launchServer(app, addr, config, configPath, pkgVersion, pkgName, callback) - ); - }); + addresses.forEach((addr) => + launchServer(app, addr, config, configPath, pkgVersion, pkgName, callback) + ); } function unlinkAddressPath(addr) { diff --git a/packages/node-api/src/experiments.ts b/packages/node-api/src/experiments.ts index 0dc41de66..627b8d424 100644 --- a/packages/node-api/src/experiments.ts +++ b/packages/node-api/src/experiments.ts @@ -1,16 +1,20 @@ -const logger = require('@verdaccio/logger'); +import buildDebug from 'debug'; -export function displayExperimentsInfoBox(experiments) { - const experimentList = Object.keys(experiments); +const debug = buildDebug('verdaccio:runtime:flags'); + +export function displayExperimentsInfoBox(flags) { + if (!flags) { + return; + } + + const experimentList = Object.keys(flags); if (experimentList.length >= 1) { - logger.logger.warn( + debug( '⚠️ 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'}` - ); + debug(` - support for %o %o`, experiment, flags[experiment] ? 'is enabled' : ' is disabled'); }); } } diff --git a/packages/plugins/aws-storage/tests/__mocks__/Config.ts b/packages/plugins/aws-storage/tests/__mocks__/Config.ts index 3671698fd..75b86497b 100644 --- a/packages/plugins/aws-storage/tests/__mocks__/Config.ts +++ b/packages/plugins/aws-storage/tests/__mocks__/Config.ts @@ -38,7 +38,7 @@ export default class Config { level: 35, }, ]; - this.self_path = './src/___tests___/__fixtures__/config.yaml'; + this.config_path = './src/___tests___/__fixtures__/config.yaml'; this.https = { enable: false, }; diff --git a/packages/plugins/google-cloud-storage/tests/partials/config.ts b/packages/plugins/google-cloud-storage/tests/partials/config.ts index 7d4ab2b7a..8582e454b 100644 --- a/packages/plugins/google-cloud-storage/tests/partials/config.ts +++ b/packages/plugins/google-cloud-storage/tests/partials/config.ts @@ -5,7 +5,7 @@ class Config implements VerdaccioConfigGoogleStorage { keyFilename: string; bucket: string; kind: string; - self_path: string; + config_path: string; secret: string; user_agent: string; server_id: string; @@ -18,7 +18,7 @@ class Config implements VerdaccioConfigGoogleStorage { $value: any; constructor() { - this.self_path = './test'; + this.config_path = './test'; this.secret = '12345'; this.uplinks = { npmjs: { diff --git a/packages/plugins/memory/test/partials/config.ts b/packages/plugins/memory/test/partials/config.ts index 12a7b3dd1..98c738f9b 100644 --- a/packages/plugins/memory/test/partials/config.ts +++ b/packages/plugins/memory/test/partials/config.ts @@ -4,7 +4,7 @@ const config: Config = { user_agent: 'string', server_id: 1234, secret: '12345', - self_path: './nowhere', + config_path: './nowhere', uplinks: { npmjs: { url: 'https://registry.npmjs.org/', diff --git a/packages/server/src/debug/index.ts b/packages/server/src/debug/index.ts index 76c9ff5b8..2e285283d 100644 --- a/packages/server/src/debug/index.ts +++ b/packages/server/src/debug/index.ts @@ -2,7 +2,7 @@ import _ from 'lodash'; import { Application } from 'express'; import { $ResponseExtend, $RequestExtend, $NextFunctionVer } from '../../types/custom'; -export default (app: Application, selfPath: string): void => { +export default (app: Application, configPath: string): void => { // Hook for tests only app.get('/-/_debug', function ( req: $RequestExtend, @@ -19,7 +19,7 @@ export default (app: Application, selfPath: string): void => { pid: process.pid, // @ts-ignore main: process.mainModule.filename, - conf: selfPath, + conf: configPath, mem: process.memoryUsage(), gc: doGarbabeCollector, }); diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 80c5e1d87..374e0342c 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -13,6 +13,7 @@ import { API_ERROR, HTTP_STATUS } from '@verdaccio/dev-commons'; import { Config as AppConfig } from '@verdaccio/config'; import { webAPI, renderWebMiddleware } from '@verdaccio/web'; +import { ConfigRuntime } from '@verdaccio/types'; import { IAuth, IBasicAuth } from '@verdaccio/auth'; import { IStorageHandler } from '@verdaccio/store'; @@ -61,7 +62,7 @@ const defineAPI = function (config: IConfig, storage: IStorageHandler): any { // Hook for tests only if (config._debug) { - hookDebug(app, config.self_path); + hookDebug(app, config.config_path); } // register middleware plugins @@ -128,7 +129,7 @@ const defineAPI = function (config: IConfig, storage: IStorageHandler): any { return app; }; -export default (async function (configHash: any): Promise { +export default (async function (configHash: ConfigRuntime): Promise { setup(configHash.logs); const config: IConfig = new AppConfig(_.cloneDeep(configHash)); // register middleware plugins diff --git a/packages/server/test/api/htpasswd b/packages/server/test/api/htpasswd new file mode 100644 index 000000000..8024f5fc2 --- /dev/null +++ b/packages/server/test/api/htpasswd @@ -0,0 +1,10 @@ +server_user_api_spec:3TdCV4iEpNFsI:autocreated 2020-11-08T10:36:37.581Z +jota_unpublish:oIj/79.KRKO6Y:autocreated 2020-11-08T10:36:37.881Z +jota_unpublish_fail:y7dutM1X0pByQ:autocreated 2020-11-08T10:36:37.934Z +jota_only_unpublish_fail:3rUkLjiXInch.:autocreated 2020-11-08T10:36:37.947Z +super_admin:yW2wIbTxWW1UA:autocreated 2020-11-08T10:36:37.953Z +any_user:VBq3LIOEN9VOY:autocreated 2020-11-08T10:36:37.971Z +jota_star:rp9KgzaAC2Uew:autocreated 2020-11-08T10:36:37.988Z +jota_deprecate:Q09PY3eVq/L1E:autocreated 2020-11-08T10:36:38.012Z +only_publish:s2pCXhzEgIupY:autocreated 2020-11-08T10:36:38.040Z +only_unpublish:+JTPy3GKmGlEE:autocreated 2020-11-08T10:36:38.046Z diff --git a/packages/server/test/api/index.spec.ts b/packages/server/test/api/index.spec.ts index f829a5ff6..11f3113e2 100644 --- a/packages/server/test/api/index.spec.ts +++ b/packages/server/test/api/index.spec.ts @@ -66,7 +66,7 @@ describe('endpoint unit test', () => { }, }, storage: store, - self_path: store, + config_path: store, uplinks: { npmjs: { url: `http://${DOMAIN_SERVERS}:${mockServerPort}`, diff --git a/packages/server/test/jwt/index.spec.ts b/packages/server/test/jwt/index.spec.ts index c5fde135b..4eafdf6fd 100644 --- a/packages/server/test/jwt/index.spec.ts +++ b/packages/server/test/jwt/index.spec.ts @@ -46,7 +46,7 @@ describe('endpoint user auth JWT unit test', () => { url: `http://${DOMAIN_SERVERS}:${mockServerPort}`, }, }, - self_path: store, + config_path: store, }, 'jwt.yaml', __dirname diff --git a/packages/server/test/package-access/index.spec.ts b/packages/server/test/package-access/index.spec.ts index 6afda6475..ac1fb2a66 100644 --- a/packages/server/test/package-access/index.spec.ts +++ b/packages/server/test/package-access/index.spec.ts @@ -19,7 +19,7 @@ describe('api with no limited access configuration', () => { const mockServerPort = 55530; const configForTest = configExample( { - self_path: store, + config_path: store, uplinks: { remote: { url: `http://${DOMAIN_SERVERS}:${mockServerPort}`, diff --git a/packages/server/test/profile/index.spec.ts b/packages/server/test/profile/index.spec.ts index fdffa02a5..ee01685a0 100644 --- a/packages/server/test/profile/index.spec.ts +++ b/packages/server/test/profile/index.spec.ts @@ -34,7 +34,7 @@ describe('endpoint user profile', () => { url: `http://${DOMAIN_SERVERS}:${mockServerPort}`, }, }, - self_path: store, + config_path: store, }, 'profile.yaml', __dirname diff --git a/packages/server/test/storage/index.spec.ts b/packages/server/test/storage/index.spec.ts index ecbd3f387..e018abdc9 100644 --- a/packages/server/test/storage/index.spec.ts +++ b/packages/server/test/storage/index.spec.ts @@ -19,7 +19,7 @@ const generateStorage = async function () { const storagePath = generateRamdonStorage(); const storageConfig = configExample( { - self_path: storagePath, + config_path: storagePath, storage: storagePath, uplinks: { npmjs: { @@ -43,7 +43,7 @@ const generateSameUplinkStorage = async function () { console.log('-->storagePath', storagePath); const storageConfig = configExample( { - self_path: storagePath, + config_path: storagePath, storage: storagePath, packages: { jquery: { diff --git a/packages/server/test/token/index.spec.ts b/packages/server/test/token/index.spec.ts index 4591c2b48..f62dcaa0a 100644 --- a/packages/server/test/token/index.spec.ts +++ b/packages/server/test/token/index.spec.ts @@ -74,7 +74,7 @@ describe('endpoint unit test', () => { const configForTest = configExample( { storage: store, - self_path: store, + config_path: store, uplinks: { npmjs: { url: `http://${DOMAIN_SERVERS}:${mockServerPort}`, diff --git a/packages/server/test/web/index.spec.ts b/packages/server/test/web/index.spec.ts index c2597a195..7c88869b7 100644 --- a/packages/server/test/web/index.spec.ts +++ b/packages/server/test/web/index.spec.ts @@ -31,7 +31,7 @@ describe('endpoint web unit test', () => { const configForTest = configExample( { storage: store, - self_path: store, + config_path: store, uplinks: { remote: { url: `http://${DOMAIN_SERVERS}:${mockServerPort}`, diff --git a/packages/store/test/local-storage.spec.ts b/packages/store/test/local-storage.spec.ts index 098ebaf7d..11ad4f21f 100644 --- a/packages/store/test/local-storage.spec.ts +++ b/packages/store/test/local-storage.spec.ts @@ -27,7 +27,7 @@ describe('LocalStorage', () => { const getStorage = (LocalStorageClass = LocalStorage) => { const config: Config = new AppConfig( configExample({ - self_path: path.join('../partials/store'), + config_path: path.join('../partials/store'), }) ); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 415ddb5ef..eac325997 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -24,12 +24,6 @@ import { NextFunction, Request, Response } from 'express'; export type StringValue = verdaccio$StringValue; -export interface StartUpConfig { - storage: string; - plugins?: string; - self_path: string; -} - // legacy should be removed in long term export interface LegacyPackageList { [key: string]: PackageAccessAddOn; diff --git a/packages/utils/src/auth-utils.ts b/packages/utils/src/auth-utils.ts index 62c025196..8be37763e 100644 --- a/packages/utils/src/auth-utils.ts +++ b/packages/utils/src/auth-utils.ts @@ -1,12 +1,5 @@ -import { ROLES, TIME_EXPIRATION_7D, DEFAULT_MIN_LIMIT_PASSWORD } from '@verdaccio/dev-commons'; -import { - RemoteUser, - AllowAccess, - PackageAccess, - Security, - APITokenOptions, - JWTOptions, -} from '@verdaccio/types'; +import { ROLES, DEFAULT_MIN_LIMIT_PASSWORD } from '@verdaccio/dev-commons'; +import { RemoteUser, AuthPackageAllow } from '@verdaccio/types'; import { VerdaccioError } from '@verdaccio/commons-api'; export interface CookieSessionToken { @@ -80,11 +73,6 @@ export type AllowAction = ( callback: AllowActionCallback ) => void; -export interface AuthPackageAllow extends PackageAccess, AllowAccess { - // TODO: this should be on @verdaccio/types - unpublish: boolean | string[]; -} - export function createSessionToken(): CookieSessionToken { const tenHoursTime = 10 * 60 * 60 * 1000; @@ -94,23 +82,6 @@ export function createSessionToken(): CookieSessionToken { }; } -const defaultWebTokenOptions: JWTOptions = { - sign: { - // The expiration token for the website is 7 days - expiresIn: TIME_EXPIRATION_7D, - }, - verify: {}, -}; - -const defaultApiTokenConf: APITokenOptions = { - legacy: true, -}; - -export const defaultSecurity: Security = { - web: defaultWebTokenOptions, - api: defaultApiTokenConf, -}; - export function getAuthenticatedMessage(user: string): string { return `you are authenticated as '${user}'`; } diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 7258e7c89..f3cfaa640 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,5 +1,4 @@ export * from './auth-utils'; -export * from './string'; export * from './utils'; export * from './crypto-utils'; export * from './replace-lodash'; diff --git a/packages/utils/src/utils.ts b/packages/utils/src/utils.ts index 34b14c722..aa760f83c 100644 --- a/packages/utils/src/utils.ts +++ b/packages/utils/src/utils.ts @@ -8,11 +8,9 @@ import { Request } from 'express'; import sanitizyReadme from '@verdaccio/readme'; import { - APP_ERROR, DEFAULT_PORT, DEFAULT_DOMAIN, DEFAULT_PROTOCOL, - CHARACTER_ENCODING, HEADERS, DIST_TAGS, DEFAULT_USER, @@ -32,16 +30,6 @@ import { getCode, } from '@verdaccio/commons-api'; -// FIXME: this is fixed, should pick the package.json or official version -const pkgVersion = '5.0.0'; -const pkgName = 'verdaccio'; - -export function getUserAgent(): string { - assert(_.isString(pkgName)); - assert(_.isString(pkgVersion)); - return `${pkgName}/${pkgVersion}`; -} - export function convertPayloadToBase64(payload: string): Buffer { return Buffer.from(payload, 'base64'); } diff --git a/packages/utils/test/utils.spec.ts b/packages/utils/test/utils.spec.ts index ef97def2a..855d43f87 100644 --- a/packages/utils/test/utils.spec.ts +++ b/packages/utils/test/utils.spec.ts @@ -2,7 +2,6 @@ import fs from 'fs'; import path from 'path'; import { DIST_TAGS, DEFAULT_USER } from '@verdaccio/dev-commons'; import { - spliceURL, validateName, convertDistRemoteToLocalTarballUrls, parseReadme, @@ -325,18 +324,6 @@ describe('Utilities', () => { }); describe('String utilities', () => { - test('should splice two strings and generate a url', () => { - const url: string = spliceURL('http://domain.com', '/-/static/logo.png'); - - expect(url).toMatch('http://domain.com/-/static/logo.png'); - }); - - test('should splice a empty strings and generate a url', () => { - const url: string = spliceURL('', '/-/static/logo.png'); - - expect(url).toMatch('/-/static/logo.png'); - }); - test('should check HTTP protocol correctly', () => { expect(isHTTPProtocol('http://domain.com/-/static/logo.png')).toBeTruthy(); expect(isHTTPProtocol('https://www.domain.com/-/static/logo.png')).toBeTruthy(); diff --git a/packages/verdaccio/test/README.md b/packages/verdaccio/test/README.md index c1459ca3f..1aa265fe9 100644 --- a/packages/verdaccio/test/README.md +++ b/packages/verdaccio/test/README.md @@ -123,7 +123,7 @@ The _mock server_ has a static storage which is located `test/unit/partials/mock > It is not possible yet to override the mocks configuration server. -> The `self_path` is a legacy prop that must to be set manually, this prop is being generated by the CLI, but running the test without the CLI force use to generate it manually. **This might change in the future**. +> The `config_path` is a legacy prop that must to be set manually, this prop is being generated by the CLI, but running the test without the CLI force use to generate it manually. **This might change in the future**. > The `const mockServerPort = 55549;` mock server must be added manually, be careful and try to define a port that is not being used by another test, there is not automation here yet. diff --git a/packages/verdaccio/test/types-test/plugins/auth/example.auth.plugin.ts b/packages/verdaccio/test/types-test/plugins/auth/example.auth.plugin.ts index e36ea85a5..09d34241b 100644 --- a/packages/verdaccio/test/types-test/plugins/auth/example.auth.plugin.ts +++ b/packages/verdaccio/test/types-test/plugins/auth/example.auth.plugin.ts @@ -79,7 +79,7 @@ class ExampleAuthCustomPlugin implements IPluginAuth<{}> { const config1: AppConfig = new Config({ storage: './storage', - self_path: '/home/sotrage', + config_path: '/home/sotrage', }); const options: PluginOptions<{}> = { diff --git a/packages/verdaccio/test/types-test/plugins/middleware/example.middleware.plugin.ts b/packages/verdaccio/test/types-test/plugins/middleware/example.middleware.plugin.ts index 6c997f74d..bb928c26a 100644 --- a/packages/verdaccio/test/types-test/plugins/middleware/example.middleware.plugin.ts +++ b/packages/verdaccio/test/types-test/plugins/middleware/example.middleware.plugin.ts @@ -41,7 +41,7 @@ export default class ExampleMiddlewarePlugin implements IPluginMiddleware<{}> { storage.removeTarball('name', 'filename', 'revision', () => {}); const config1: AppConfig = new Config({ storage: './storage', - self_path: '/home/sotrage', + config_path: '/home/sotrage', }); const add: IUploadTarball = storage.addTarball('name', 'filename'); storage.getTarball('name', 'filename'); diff --git a/packages/verdaccio/test/types-test/plugins/storage/example.storage.plugin.ts b/packages/verdaccio/test/types-test/plugins/storage/example.storage.plugin.ts index ec551ed22..a32c3e6cd 100644 --- a/packages/verdaccio/test/types-test/plugins/storage/example.storage.plugin.ts +++ b/packages/verdaccio/test/types-test/plugins/storage/example.storage.plugin.ts @@ -146,7 +146,7 @@ export default ExampleStoragePlugin; const config1: AppConfig = new Config({ storage: './storage', - self_path: '/home/sotrage', + config_path: '/home/sotrage', }); const storage = new ExampleStoragePlugin(config1, logger.child()); diff --git a/packages/verdaccio/test/unit/__helper/__template/api.__test.ts b/packages/verdaccio/test/unit/__helper/__template/api.__test.ts index c28433033..a92b7e733 100644 --- a/packages/verdaccio/test/unit/__helper/__template/api.__test.ts +++ b/packages/verdaccio/test/unit/__helper/__template/api.__test.ts @@ -52,7 +52,7 @@ describe('endpoint example unit test', () => { }, }, // 6. The self_path is important be the same as the store - self_path: store, + config_path: store, // 7. Define the location of the .htpasswd file, this is relative to self_path. auth: { htpasswd: { diff --git a/packages/web/src/endpoint/user.ts b/packages/web/src/endpoint/user.ts index ac14e3c62..4d48a894a 100644 --- a/packages/web/src/endpoint/user.ts +++ b/packages/web/src/endpoint/user.ts @@ -6,7 +6,6 @@ import { Config, RemoteUser, JWTSignOptions } from '@verdaccio/types'; import { API_ERROR, APP_ERROR, HTTP_STATUS } from '@verdaccio/dev-commons'; import { IAuth } from '@verdaccio/auth'; import { validatePassword, ErrorCode } from '@verdaccio/utils'; -import { getSecurity } from '@verdaccio/auth'; import { $NextFunctionVer } from './package'; function addUserAuthApi(route: Router, auth: IAuth, config: Config): void { @@ -22,7 +21,7 @@ function addUserAuthApi(route: Router, auth: IAuth, config: Config): void { next(ErrorCode.getCode(errorCode, err.message)); } else { req.remote_user = user; - const jWTSignOptions: JWTSignOptions = getSecurity(config).web.sign; + const jWTSignOptions: JWTSignOptions = config.security.web.sign; next({ token: await auth.jwtEncrypt(user, jWTSignOptions), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 162ed5709..bff4d067a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -510,6 +510,7 @@ importers: '@verdaccio/server': 'link:../server' '@verdaccio/utils': 'link:../utils' core-js: 3.6.5 + debug: 4.2.0 lodash: 4.17.20 selfsigned: 1.10.7 devDependencies: @@ -524,6 +525,7 @@ importers: '@verdaccio/types': 'workspace:*' '@verdaccio/utils': 'workspace:5.0.0-alpha.0' core-js: ^3.6.5 + debug: ^4.2.0 lodash: ^4.17.20 selfsigned: 1.10.7 packages/plugins/active-directory: