@verdaccio/auth 9.0.0-next-9.26 → 9.0.0-next-9.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/auth.d.ts +1 -0
- package/build/auth.js +34 -37
- package/build/auth.js.map +1 -1
- package/build/auth.mjs +37 -40
- package/build/auth.mjs.map +1 -1
- package/build/index.js +0 -1
- package/build/index.mjs +2 -2
- package/build/utils.d.ts +0 -1
- package/build/utils.js +1 -8
- package/build/utils.js.map +1 -1
- package/build/utils.mjs +3 -9
- package/build/utils.mjs.map +1 -1
- package/package.json +8 -8
package/build/auth.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ declare class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBas
|
|
|
34
34
|
apiJWTmiddleware(): any;
|
|
35
35
|
private handleJWTAPIMiddleware;
|
|
36
36
|
private handleAESMiddleware;
|
|
37
|
+
private getJWTRemoteUserFromBearer;
|
|
37
38
|
private enqueueLegacyAuthCacheWaiter;
|
|
38
39
|
private resolveLegacyAuthCacheWaiters;
|
|
39
40
|
private isLegacyAuthCacheEnabled;
|
package/build/auth.js
CHANGED
|
@@ -282,7 +282,7 @@ var Auth = class {
|
|
|
282
282
|
req.pause();
|
|
283
283
|
const next = function(err) {
|
|
284
284
|
req.resume();
|
|
285
|
-
if (err)
|
|
285
|
+
if (err) return _next(err);
|
|
286
286
|
return _next();
|
|
287
287
|
};
|
|
288
288
|
const remoteUser = (0, _verdaccio_config.createAnonymousRemoteUser)();
|
|
@@ -309,40 +309,14 @@ var Auth = class {
|
|
|
309
309
|
}
|
|
310
310
|
handleJWTAPIMiddleware(req, security, secret, authorization, next) {
|
|
311
311
|
debug$1("handle JWT api middleware");
|
|
312
|
-
const
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (!parsedCredentials) {
|
|
318
|
-
debug$1("invalid basic credentials format (missing colon separator)");
|
|
319
|
-
next(_verdaccio_core.errorUtils.getBadRequest(_verdaccio_core.API_ERROR.BAD_USERNAME_PASSWORD));
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
const { user, password } = parsedCredentials;
|
|
323
|
-
debug$1("authenticating %o", user);
|
|
324
|
-
this.authenticate(user, password, (err, user) => {
|
|
325
|
-
if (!err) {
|
|
326
|
-
debug$1("generating a remote user");
|
|
327
|
-
req.remote_user = user;
|
|
328
|
-
next();
|
|
329
|
-
} else {
|
|
330
|
-
debug$1("generating anonymous user");
|
|
331
|
-
req.remote_user = (0, _verdaccio_config.createAnonymousRemoteUser)();
|
|
332
|
-
next(err);
|
|
333
|
-
}
|
|
334
|
-
});
|
|
312
|
+
const credentials = require_utils.getMiddlewareCredentials(security, secret, authorization);
|
|
313
|
+
if (credentials) {
|
|
314
|
+
req.remote_user = credentials;
|
|
315
|
+
debug$1("generating a remote user");
|
|
316
|
+
next();
|
|
335
317
|
} else {
|
|
336
|
-
debug$1("
|
|
337
|
-
|
|
338
|
-
if (credentials) {
|
|
339
|
-
req.remote_user = credentials;
|
|
340
|
-
debug$1("generating a remote user");
|
|
341
|
-
next();
|
|
342
|
-
} else {
|
|
343
|
-
debug$1("jwt invalid token");
|
|
344
|
-
next(_verdaccio_core.errorUtils.getForbidden(_verdaccio_core.API_ERROR.BAD_USERNAME_PASSWORD));
|
|
345
|
-
}
|
|
318
|
+
debug$1("jwt invalid token");
|
|
319
|
+
next(_verdaccio_core.errorUtils.getUnauthorized(_verdaccio_core.API_ERROR.BAD_USERNAME_PASSWORD));
|
|
346
320
|
}
|
|
347
321
|
}
|
|
348
322
|
handleAESMiddleware(req, security, secret, authorization, next) {
|
|
@@ -371,7 +345,7 @@ var Auth = class {
|
|
|
371
345
|
} else {
|
|
372
346
|
req.remote_user = (0, _verdaccio_config.createAnonymousRemoteUser)();
|
|
373
347
|
debug$1("generating anonymous user");
|
|
374
|
-
next(err || _verdaccio_core.errorUtils.
|
|
348
|
+
next(err || _verdaccio_core.errorUtils.getUnauthorized(_verdaccio_core.API_ERROR.BAD_USERNAME_PASSWORD));
|
|
375
349
|
}
|
|
376
350
|
};
|
|
377
351
|
if (this.enqueueLegacyAuthCacheWaiter(cacheKey, applyAuthResult)) return;
|
|
@@ -390,8 +364,28 @@ var Auth = class {
|
|
|
390
364
|
onAuthComplete(_verdaccio_core.errorUtils.getInternalError(err?.message));
|
|
391
365
|
}
|
|
392
366
|
} else {
|
|
367
|
+
const remoteUser = this.getJWTRemoteUserFromBearer(authorization);
|
|
368
|
+
if (remoteUser) {
|
|
369
|
+
req.remote_user = remoteUser;
|
|
370
|
+
debug$1("generating a remote user from jwt bearer");
|
|
371
|
+
return next();
|
|
372
|
+
}
|
|
393
373
|
debug$1("legacy invalid header");
|
|
394
|
-
return next(_verdaccio_core.errorUtils.
|
|
374
|
+
return next(_verdaccio_core.errorUtils.getUnauthorized(_verdaccio_core.API_ERROR.BAD_USERNAME_PASSWORD));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
getJWTRemoteUserFromBearer(authorization) {
|
|
378
|
+
const { scheme, token } = require_utils.parseAuthTokenHeader(authorization);
|
|
379
|
+
if (scheme.toUpperCase() !== _verdaccio_core.TOKEN_BEARER.toUpperCase() || !token) return;
|
|
380
|
+
let credentials;
|
|
381
|
+
try {
|
|
382
|
+
credentials = require_utils.verifyJWTPayload(token, this.config.secret, this.config.security);
|
|
383
|
+
} catch {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (this._isRemoteUserValid(credentials)) {
|
|
387
|
+
const { name, groups } = credentials;
|
|
388
|
+
return (0, _verdaccio_config.createRemoteUser)(name, groups);
|
|
395
389
|
}
|
|
396
390
|
}
|
|
397
391
|
enqueueLegacyAuthCacheWaiter(cacheKey, waiter) {
|
|
@@ -472,7 +466,10 @@ var Auth = class {
|
|
|
472
466
|
return _next();
|
|
473
467
|
};
|
|
474
468
|
const { authorization } = req.headers;
|
|
475
|
-
if ((0, lodash_es.isNil)(authorization))
|
|
469
|
+
if ((0, lodash_es.isNil)(authorization)) {
|
|
470
|
+
req.remote_user = (0, _verdaccio_config.createAnonymousRemoteUser)();
|
|
471
|
+
return next();
|
|
472
|
+
}
|
|
476
473
|
if (!require_utils.isAuthHeaderValid(authorization)) return next(_verdaccio_core.errorUtils.getBadRequest(_verdaccio_core.API_ERROR.BAD_AUTH_HEADER));
|
|
477
474
|
const token = (authorization || "").replace(`${_verdaccio_core.TOKEN_BEARER} `, "");
|
|
478
475
|
if (!token) return next();
|
package/build/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","names":[],"sources":["../src/auth.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { filter, isEmpty, isNil, isUndefined } from 'lodash-es';\nimport { createHash } from 'node:crypto';\nimport { HTPasswd } from 'verdaccio-htpasswd';\n\nimport { createAnonymousRemoteUser, createRemoteUser } from '@verdaccio/config';\nimport type { VerdaccioError, pluginUtils } from '@verdaccio/core';\nimport {\n API_ERROR,\n PLUGIN_CATEGORY,\n PLUGIN_PREFIX,\n SUPPORT_ERRORS,\n TOKEN_BASIC,\n TOKEN_BEARER,\n authUtils,\n errorUtils,\n pluginUtils as pluginSanity,\n warningUtils,\n} from '@verdaccio/core';\nimport { asyncLoadPlugin } from '@verdaccio/loaders';\nimport { aesEncrypt, parseBasicPayload, signPayload } from '@verdaccio/signature';\nimport type {\n AllowAccess,\n Callback,\n Config,\n JWTSignOptions,\n Logger,\n PackageAccess,\n RemoteUser,\n Security,\n} from '@verdaccio/types';\n\nimport type {\n $RequestExtend,\n $ResponseExtend,\n AESPayload,\n IAuthMiddleware,\n NextFunction,\n TokenEncryption,\n} from './types';\nimport {\n convertPayloadToBase64,\n getDefaultPluginMethods,\n getMiddlewareCredentials,\n isAESLegacy,\n isAuthHeaderValid,\n parseAuthTokenHeader,\n verifyJWTPayload,\n} from './utils';\n\nconst debug = buildDebug('verdaccio:auth');\ntype LegacyAuthCacheEntry = {\n expiresAt: number;\n user: RemoteUser;\n};\n\ntype LegacyAuthCacheWaiter = (err: VerdaccioError | null, user?: RemoteUser) => void;\n\nfunction cloneRemoteUser(user: RemoteUser): RemoteUser {\n return {\n ...user,\n groups: user.groups ? [...user.groups] : user.groups,\n real_groups: user.real_groups ? [...user.real_groups] : user.real_groups,\n token: user.token ? { ...user.token } : user.token,\n };\n}\n\nclass Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {\n public config: Config;\n public secret: string;\n public logger: Logger;\n public plugins: pluginUtils.Auth<Config>[];\n public options: { legacyMergeConfigs: boolean };\n private legacyAuthCache: Map<string, LegacyAuthCacheEntry>;\n private legacyAuthCacheWaiters: Map<string, LegacyAuthCacheWaiter[]>;\n\n public constructor(config: Config, logger: Logger, options = { legacyMergeConfigs: false }) {\n this.config = config;\n this.secret = config.secret;\n this.logger = logger;\n this.plugins = [];\n this.options = options;\n this.legacyAuthCache = new Map();\n this.legacyAuthCacheWaiters = new Map();\n if (!this.secret) {\n throw new TypeError('secret it is required value on initialize the auth class');\n }\n }\n\n public async init() {\n let plugins = await this.loadPlugin();\n\n debug('auth plugins found %s', plugins.length);\n // Missing auth config or no loaded plugins -> load default htpasswd plugin\n // Empty auth config (null) -> just use fallback methods\n if (this.config.auth !== null && (!plugins || plugins.length === 0)) {\n plugins = this.loadDefaultPlugin();\n }\n this.plugins = plugins;\n\n this.applyFallbackPluginMethods();\n }\n\n private loadDefaultPlugin() {\n debug('load default auth plugin');\n let authPlugin;\n try {\n authPlugin = new HTPasswd(\n { file: './htpasswd' },\n {\n config: this.config,\n logger: this.logger,\n }\n );\n this.logger.info(\n { name: 'verdaccio-htpasswd', pluginCategory: PLUGIN_CATEGORY.AUTHENTICATION },\n 'plugin @{name} successfully loaded (@{pluginCategory})'\n );\n } catch (error: any) {\n debug('error on loading auth htpasswd plugin stack: %o', error);\n this.logger.info({}, 'no auth plugin has been found');\n return [];\n }\n\n return [authPlugin];\n }\n\n private async loadPlugin() {\n return asyncLoadPlugin<pluginUtils.Auth<Config>>(\n this.config.auth,\n {\n config: this.config,\n logger: this.logger,\n },\n pluginSanity.authSanityCheck,\n this.options.legacyMergeConfigs,\n this.config?.server?.pluginPrefix ?? PLUGIN_PREFIX,\n PLUGIN_CATEGORY.AUTHENTICATION\n );\n }\n\n private applyFallbackPluginMethods(): void {\n this.plugins.push(getDefaultPluginMethods(this.logger));\n }\n\n public changePassword(\n username: string,\n password: string,\n newPassword: string,\n cb: Callback\n ): void {\n const validPlugins = filter(\n this.plugins,\n (plugin) => typeof plugin.changePassword === 'function'\n );\n\n if (isEmpty(validPlugins)) {\n return cb(errorUtils.getInternalError(SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));\n }\n\n for (const plugin of validPlugins) {\n if (isNil(plugin) || typeof plugin.changePassword !== 'function') {\n debug('auth plugin does not implement changePassword, trying next one');\n continue;\n } else {\n debug('updating password for %o', username);\n plugin.changePassword!(username, password, newPassword, (err, profile): void => {\n if (err) {\n this.logger.error(\n { username, err },\n `An error has been produced\n updating the password for @{username}. Error: @{err.message}`\n );\n return cb(err);\n }\n\n debug('updated password for %o was successful', username);\n return cb(null, profile);\n });\n }\n }\n }\n\n public async invalidateToken(token: string) {\n // eslint-disable-next-line no-console\n console.log('invalidate token pending to implement', token);\n return Promise.resolve();\n }\n\n public authenticate(\n username: string,\n password: string,\n cb: (error: VerdaccioError | null, user?: RemoteUser) => void\n ): void {\n const plugins = this.plugins.slice(0);\n (function next(): void {\n const plugin = plugins.shift();\n\n if (typeof plugin?.authenticate !== 'function') {\n return next();\n }\n\n debug('authenticating %o', username);\n plugin.authenticate(username, password, function (err: VerdaccioError | null, groups): void {\n if (err) {\n debug('authenticating for user %o failed. Error: %o', username, err?.message);\n return cb(err);\n }\n\n // Expect: SKIP if groups is falsey and not an array\n // with at least one item (truthy length)\n // Expect: CONTINUE otherwise (will error if groups is not\n // an array, but this is current behavior)\n // Caveat: STRING (if valid) will pass successfully\n // bug give unexpected results\n // Info: Cannot use `== false to check falsey values`\n if (!!groups && groups.length !== 0) {\n // TODO: create a better understanding of expectations\n if (typeof groups === 'string') {\n throw new TypeError('plugin group error: invalid type for function');\n }\n const isGroupValid: boolean = Array.isArray(groups);\n if (!isGroupValid) {\n throw new TypeError(API_ERROR.BAD_FORMAT_USER_GROUP);\n }\n\n debug('authentication for user %o was successfully. Groups: %o', username, groups);\n return cb(err, createRemoteUser(username, groups));\n }\n next();\n });\n })();\n }\n\n public add_user(\n user: string,\n password: string,\n cb: (error: VerdaccioError | null, user?: RemoteUser) => void\n ): void {\n const self = this;\n const plugins = this.plugins.slice(0);\n debug('add user %o', user);\n\n (function next(): void {\n let method = 'adduser';\n const plugin = plugins.shift();\n // @ts-expect-error future major (7.x) should remove this section\n if (typeof plugin.adduser === 'undefined' && typeof plugin.add_user === 'function') {\n method = 'add_user';\n warningUtils.emit(warningUtils.Codes.VERWAR006);\n }\n // @ts-ignore\n if (typeof plugin[method] !== 'function') {\n next();\n } else {\n // TODO: replace by adduser whenever add_user deprecation method has been removed\n // @ts-ignore\n plugin[method](\n user,\n password,\n function (err: VerdaccioError | null, ok?: boolean | string): void {\n if (err) {\n debug('the user %o could not be added. Error: %o', user, err?.message);\n return cb(err);\n }\n if (ok) {\n debug('the user %o has been added', user);\n return self.authenticate(user, password, cb);\n }\n debug('user could not be added, skip to next auth plugin');\n next();\n }\n );\n }\n })();\n }\n\n /**\n * Allow user to access a package.\n */\n public allow_access(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: pluginUtils.AccessCallback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n ) as AllowAccess & PackageAccess;\n\n debug('check access permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_access !== 'function') {\n debug('plugin does not implement allow_access');\n return next();\n }\n\n plugin.allow_access(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden access. Error: %o', err);\n return callback(err);\n }\n\n if (ok) {\n debug('access was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `access was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('access was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate access denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n public allow_unpublish(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: Callback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n );\n\n debug('check unpublish permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_unpublish !== 'function') {\n debug('plugin does not implement allow_unpublish');\n return next();\n }\n\n plugin.allow_unpublish(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden unpublish. Error: %o', err);\n return callback(err);\n }\n\n // The following is different from the allow_access and allow_publish implementations:\n // If the packages config is missing an entry for \"unpublish\", the built-in default method\n // (or a plugin) will return undefined, which will trigger the allow_publish fallback.\n // (see utils.ts, handleSpecialUnpublish, callback(null, undefined))\n if (isNil(ok) === true) {\n debug('bypass unpublish for %o, publish will handle the access', packageName);\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `bypass unpublish for @{name} by @{user}, publish will handle the access`\n );\n return this.allow_publish({ packageName, packageVersion }, user, callback);\n }\n\n if (ok) {\n debug('unpublish was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `unpublish was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('unpublish was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate unpublish denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n /**\n * Allow user to publish a package.\n */\n public allow_publish(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: Callback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n );\n\n debug('check publish permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_publish !== 'function') {\n debug('plugin does not implement allow_publish');\n return next();\n }\n\n plugin.allow_publish(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden publish. Error: %o', err);\n return callback(err);\n }\n\n if (ok) {\n debug('publish was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `publish was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('publish was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate publish denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n public apiJWTmiddleware(): any {\n debug('jwt middleware');\n const plugins = this.plugins.slice(0);\n const helpers = { createAnonymousRemoteUser, createRemoteUser };\n for (const plugin of plugins) {\n if (plugin.apiJWTmiddleware) {\n return plugin.apiJWTmiddleware(helpers);\n }\n }\n\n return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction) => {\n req.pause();\n const next = function (err?: VerdaccioError): NextFunction {\n req.resume();\n // uncomment this to reject users with bad auth headers\n // return _next.apply(null, arguments)\n // swallow error, user remains unauthorized\n // set remoteUserError to indicate that user was attempting authentication\n if (err) {\n req.remote_user.error = err.message;\n }\n\n return _next() as unknown as NextFunction;\n };\n\n // FUTURE: disabled, not removed yet but seems unreacable code\n // if (this._isRemoteUserValid(req.remote_user)) {\n // debug('jwt has a valid authentication header');\n // return next();\n // }\n\n // in case auth header does not exist we return anonymous function\n const remoteUser = createAnonymousRemoteUser();\n req.remote_user = remoteUser;\n res.locals.remote_user = remoteUser;\n\n const { authorization } = req.headers;\n if (isNil(authorization)) {\n debug('jwt, authentication header is missing');\n return next();\n }\n\n if (!isAuthHeaderValid(authorization)) {\n debug('api middleware authentication heather is invalid');\n return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));\n }\n const { secret, security } = this.config;\n\n if (isAESLegacy(security)) {\n debug('api middleware using legacy auth token');\n this.handleAESMiddleware(req, security, secret, authorization, next);\n } else {\n debug('api middleware using JWT auth token');\n this.handleJWTAPIMiddleware(req, security, secret, authorization, next);\n }\n };\n }\n\n private handleJWTAPIMiddleware(\n req: $RequestExtend,\n security: Security,\n secret: string,\n authorization: string,\n next: any\n ): void {\n debug('handle JWT api middleware');\n const { scheme, token } = parseAuthTokenHeader(authorization);\n if (scheme.toUpperCase() === TOKEN_BASIC.toUpperCase()) {\n debug('handle basic token');\n // this should happen when client tries to login with an existing user\n const credentials = convertPayloadToBase64(token).toString();\n const parsedCredentials = parseBasicPayload(credentials);\n if (!parsedCredentials) {\n debug('invalid basic credentials format (missing colon separator)');\n next(errorUtils.getBadRequest(API_ERROR.BAD_USERNAME_PASSWORD));\n return;\n }\n const { user, password } = parsedCredentials as AESPayload;\n debug('authenticating %o', user);\n this.authenticate(user, password, (err: VerdaccioError | null, user): void => {\n if (!err) {\n debug('generating a remote user');\n req.remote_user = user;\n next();\n } else {\n debug('generating anonymous user');\n req.remote_user = createAnonymousRemoteUser();\n next(err);\n }\n });\n } else {\n debug('handle jwt token');\n const credentials: any = getMiddlewareCredentials(security, secret, authorization);\n if (credentials) {\n // if the signature is valid we rely on it\n req.remote_user = credentials;\n debug('generating a remote user');\n next();\n } else {\n // with JWT throw 401\n debug('jwt invalid token');\n next(errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));\n }\n }\n }\n\n private handleAESMiddleware(\n req: $RequestExtend,\n security: Security,\n secret: string,\n authorization: string,\n next: Function\n ): void {\n debug('handle legacy api middleware');\n debug('api middleware has a secret? %o', typeof secret === 'string');\n debug('api middleware authorization %o', typeof authorization === 'string');\n const credentials: any = getMiddlewareCredentials(security, secret, authorization);\n debug('api middleware credentials %o', credentials?.name);\n if (credentials) {\n const cacheKey = this.getLegacyAuthCacheKey(authorization);\n const cachedUser = this.getLegacyAuthCacheEntry(cacheKey);\n if (cachedUser) {\n req.remote_user = cachedUser;\n debug('generating cached remote user');\n return next();\n }\n\n const { user, password } = credentials;\n const applyAuthResult = (err: VerdaccioError | null, user?: RemoteUser): void => {\n if (!err && user) {\n req.remote_user = credentials.tokenKey\n ? { ...user, token: { key: credentials.tokenKey } }\n : user;\n debug('generating a remote user');\n next();\n } else {\n req.remote_user = createAnonymousRemoteUser();\n debug('generating anonymous user');\n next(err || errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));\n }\n };\n // concurrent requests for the same token wait for the in-flight one\n if (this.enqueueLegacyAuthCacheWaiter(cacheKey, applyAuthResult)) {\n return;\n }\n\n debug('authenticating %o', user);\n const onAuthComplete = (err: VerdaccioError | null, user?: RemoteUser): void => {\n // only the leader writes the cache; waiters just reuse its result\n if (!err && user) {\n this.setLegacyAuthCacheEntry(\n cacheKey,\n credentials.tokenKey ? { ...user, token: { key: credentials.tokenKey } } : user\n );\n }\n applyAuthResult(err, user);\n this.resolveLegacyAuthCacheWaiters(cacheKey, err, user);\n };\n try {\n this.authenticate(user, password, onAuthComplete);\n } catch (err: any) {\n onAuthComplete(errorUtils.getInternalError(err?.message));\n }\n } else {\n // we force npm client to ask again with basic authentication\n debug('legacy invalid header');\n return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));\n }\n }\n\n private enqueueLegacyAuthCacheWaiter(\n cacheKey: string | void,\n waiter: LegacyAuthCacheWaiter\n ): boolean {\n if (!cacheKey) {\n return false;\n }\n\n const waiters = this.legacyAuthCacheWaiters.get(cacheKey);\n if (!waiters) {\n this.legacyAuthCacheWaiters.set(cacheKey, []);\n return false;\n }\n\n waiters.push(waiter);\n return true;\n }\n\n private resolveLegacyAuthCacheWaiters(\n cacheKey: string | void,\n err: VerdaccioError | null,\n user?: RemoteUser\n ): void {\n if (!cacheKey) {\n return;\n }\n\n const waiters = this.legacyAuthCacheWaiters.get(cacheKey);\n this.legacyAuthCacheWaiters.delete(cacheKey);\n if (!waiters) {\n return;\n }\n\n for (const waiter of waiters) {\n waiter(err, user);\n }\n }\n\n private isLegacyAuthCacheEnabled(): boolean {\n // opt-in: disabled unless explicitly turned on via config\n return this.config.server?.legacyAuthCache?.enabled === true;\n }\n\n private getLegacyAuthCacheTtlMs(): number {\n const ttlMs = this.config.server?.legacyAuthCache?.ttlMs;\n return typeof ttlMs === 'number' && ttlMs > 0 ? ttlMs : 30 * 1000;\n }\n\n private getLegacyAuthCacheMaxEntries(): number {\n const maxEntries = this.config.server?.legacyAuthCache?.maxEntries;\n return typeof maxEntries === 'number' && maxEntries > 0 ? maxEntries : 1000;\n }\n\n private getLegacyAuthCacheKey(authorization: string): string | void {\n if (!this.isLegacyAuthCacheEnabled()) {\n return;\n }\n\n const { scheme } = parseAuthTokenHeader(authorization);\n if (scheme.toUpperCase() !== TOKEN_BEARER.toUpperCase()) {\n return;\n }\n\n return createHash('sha256').update(authorization).digest('hex');\n }\n\n private getLegacyAuthCacheEntry(cacheKey: string | void): RemoteUser | void {\n if (!cacheKey) {\n return;\n }\n\n const entry = this.legacyAuthCache.get(cacheKey);\n if (!entry) {\n return;\n }\n\n if (entry.expiresAt <= Date.now()) {\n this.legacyAuthCache.delete(cacheKey);\n return;\n }\n\n this.legacyAuthCache.delete(cacheKey);\n this.legacyAuthCache.set(cacheKey, entry);\n return cloneRemoteUser(entry.user);\n }\n\n private setLegacyAuthCacheEntry(cacheKey: string | void, user: RemoteUser): void {\n if (!cacheKey) {\n return;\n }\n\n this.legacyAuthCache.set(cacheKey, {\n expiresAt: Date.now() + this.getLegacyAuthCacheTtlMs(),\n user: cloneRemoteUser(user),\n });\n\n const maxEntries = this.getLegacyAuthCacheMaxEntries();\n while (this.legacyAuthCache.size > maxEntries) {\n const oldestKey = this.legacyAuthCache.keys().next().value;\n if (!oldestKey) {\n break;\n }\n this.legacyAuthCache.delete(oldestKey);\n }\n }\n\n private _isRemoteUserValid(remote_user?: RemoteUser): boolean {\n return isUndefined(remote_user) === false && isUndefined(remote_user?.name) === false;\n }\n\n /**\n * JWT middleware for WebUI\n */\n public webUIJWTmiddleware() {\n return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction): void => {\n if (this._isRemoteUserValid(req.remote_user)) {\n return _next();\n }\n\n req.pause();\n const next = (err: VerdaccioError | void): void => {\n req.resume();\n if (err) {\n req.remote_user.error = err.message;\n res.status(err.statusCode).send(err.message);\n }\n\n return _next();\n };\n\n const { authorization } = req.headers;\n if (isNil(authorization)) {\n return next();\n }\n\n if (!isAuthHeaderValid(authorization)) {\n return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));\n }\n\n const token = (authorization || '').replace(`${TOKEN_BEARER} `, '');\n if (!token) {\n return next();\n }\n\n let credentials: RemoteUser | undefined;\n try {\n credentials = verifyJWTPayload(token, this.config.secret, this.config.security);\n } catch {\n // FIXME: intended behaviour, do we want it?\n }\n\n if (this._isRemoteUserValid(credentials)) {\n const { name, groups } = credentials as RemoteUser;\n req.remote_user = createRemoteUser(name as string, groups);\n } else {\n req.remote_user = createAnonymousRemoteUser();\n }\n\n next();\n };\n }\n\n public async jwtEncrypt(user: RemoteUser, signOptions: JWTSignOptions): Promise<string> {\n const { real_groups, name, groups, token: tokenMetadata } = user;\n debug('jwt encrypt %o', name);\n const realGroupsValidated = isNil(real_groups) ? [] : real_groups;\n const groupedGroups = isNil(groups)\n ? real_groups\n : Array.from(new Set([...groups.concat(realGroupsValidated)]));\n const payload: RemoteUser = {\n real_groups: realGroupsValidated,\n name,\n groups: groupedGroups,\n };\n if (tokenMetadata?.key) {\n payload.token = { key: tokenMetadata.key };\n }\n const signedToken: string = await signPayload(\n payload,\n this.secret,\n signOptions as Parameters<typeof signPayload>[2]\n );\n\n return signedToken;\n }\n\n /**\n * Encrypt a string.\n */\n public aesEncrypt(value: string): string | void {\n debug('signing with aes encryption');\n const token = aesEncrypt(value, this.secret);\n return token;\n }\n}\n\nexport { Auth };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"auth.js","names":[],"sources":["../src/auth.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { filter, isEmpty, isNil, isUndefined } from 'lodash-es';\nimport { createHash } from 'node:crypto';\nimport { HTPasswd } from 'verdaccio-htpasswd';\n\nimport { createAnonymousRemoteUser, createRemoteUser } from '@verdaccio/config';\nimport type { VerdaccioError, pluginUtils } from '@verdaccio/core';\nimport {\n API_ERROR,\n PLUGIN_CATEGORY,\n PLUGIN_PREFIX,\n SUPPORT_ERRORS,\n TOKEN_BEARER,\n authUtils,\n errorUtils,\n pluginUtils as pluginSanity,\n warningUtils,\n} from '@verdaccio/core';\nimport { asyncLoadPlugin } from '@verdaccio/loaders';\nimport { aesEncrypt, signPayload } from '@verdaccio/signature';\nimport type {\n AllowAccess,\n Callback,\n Config,\n JWTSignOptions,\n Logger,\n PackageAccess,\n RemoteUser,\n Security,\n} from '@verdaccio/types';\n\nimport type {\n $RequestExtend,\n $ResponseExtend,\n IAuthMiddleware,\n NextFunction,\n TokenEncryption,\n} from './types';\nimport {\n getDefaultPluginMethods,\n getMiddlewareCredentials,\n isAESLegacy,\n isAuthHeaderValid,\n parseAuthTokenHeader,\n verifyJWTPayload,\n} from './utils';\n\nconst debug = buildDebug('verdaccio:auth');\ntype LegacyAuthCacheEntry = {\n expiresAt: number;\n user: RemoteUser;\n};\n\ntype LegacyAuthCacheWaiter = (err: VerdaccioError | null, user?: RemoteUser) => void;\n\nfunction cloneRemoteUser(user: RemoteUser): RemoteUser {\n return {\n ...user,\n groups: user.groups ? [...user.groups] : user.groups,\n real_groups: user.real_groups ? [...user.real_groups] : user.real_groups,\n token: user.token ? { ...user.token } : user.token,\n };\n}\n\nclass Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {\n public config: Config;\n public secret: string;\n public logger: Logger;\n public plugins: pluginUtils.Auth<Config>[];\n public options: { legacyMergeConfigs: boolean };\n private legacyAuthCache: Map<string, LegacyAuthCacheEntry>;\n private legacyAuthCacheWaiters: Map<string, LegacyAuthCacheWaiter[]>;\n\n public constructor(config: Config, logger: Logger, options = { legacyMergeConfigs: false }) {\n this.config = config;\n this.secret = config.secret;\n this.logger = logger;\n this.plugins = [];\n this.options = options;\n this.legacyAuthCache = new Map();\n this.legacyAuthCacheWaiters = new Map();\n if (!this.secret) {\n throw new TypeError('secret it is required value on initialize the auth class');\n }\n }\n\n public async init() {\n let plugins = await this.loadPlugin();\n\n debug('auth plugins found %s', plugins.length);\n // Missing auth config or no loaded plugins -> load default htpasswd plugin\n // Empty auth config (null) -> just use fallback methods\n if (this.config.auth !== null && (!plugins || plugins.length === 0)) {\n plugins = this.loadDefaultPlugin();\n }\n this.plugins = plugins;\n\n this.applyFallbackPluginMethods();\n }\n\n private loadDefaultPlugin() {\n debug('load default auth plugin');\n let authPlugin;\n try {\n authPlugin = new HTPasswd(\n { file: './htpasswd' },\n {\n config: this.config,\n logger: this.logger,\n }\n );\n this.logger.info(\n { name: 'verdaccio-htpasswd', pluginCategory: PLUGIN_CATEGORY.AUTHENTICATION },\n 'plugin @{name} successfully loaded (@{pluginCategory})'\n );\n } catch (error: any) {\n debug('error on loading auth htpasswd plugin stack: %o', error);\n this.logger.info({}, 'no auth plugin has been found');\n return [];\n }\n\n return [authPlugin];\n }\n\n private async loadPlugin() {\n return asyncLoadPlugin<pluginUtils.Auth<Config>>(\n this.config.auth,\n {\n config: this.config,\n logger: this.logger,\n },\n pluginSanity.authSanityCheck,\n this.options.legacyMergeConfigs,\n this.config?.server?.pluginPrefix ?? PLUGIN_PREFIX,\n PLUGIN_CATEGORY.AUTHENTICATION\n );\n }\n\n private applyFallbackPluginMethods(): void {\n this.plugins.push(getDefaultPluginMethods(this.logger));\n }\n\n public changePassword(\n username: string,\n password: string,\n newPassword: string,\n cb: Callback\n ): void {\n const validPlugins = filter(\n this.plugins,\n (plugin) => typeof plugin.changePassword === 'function'\n );\n\n if (isEmpty(validPlugins)) {\n return cb(errorUtils.getInternalError(SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));\n }\n\n for (const plugin of validPlugins) {\n if (isNil(plugin) || typeof plugin.changePassword !== 'function') {\n debug('auth plugin does not implement changePassword, trying next one');\n continue;\n } else {\n debug('updating password for %o', username);\n plugin.changePassword!(username, password, newPassword, (err, profile): void => {\n if (err) {\n this.logger.error(\n { username, err },\n `An error has been produced\n updating the password for @{username}. Error: @{err.message}`\n );\n return cb(err);\n }\n\n debug('updated password for %o was successful', username);\n return cb(null, profile);\n });\n }\n }\n }\n\n public async invalidateToken(token: string) {\n // eslint-disable-next-line no-console\n console.log('invalidate token pending to implement', token);\n return Promise.resolve();\n }\n\n public authenticate(\n username: string,\n password: string,\n cb: (error: VerdaccioError | null, user?: RemoteUser) => void\n ): void {\n const plugins = this.plugins.slice(0);\n (function next(): void {\n const plugin = plugins.shift();\n\n if (typeof plugin?.authenticate !== 'function') {\n return next();\n }\n\n debug('authenticating %o', username);\n plugin.authenticate(username, password, function (err: VerdaccioError | null, groups): void {\n if (err) {\n debug('authenticating for user %o failed. Error: %o', username, err?.message);\n return cb(err);\n }\n\n // Expect: SKIP if groups is falsey and not an array\n // with at least one item (truthy length)\n // Expect: CONTINUE otherwise (will error if groups is not\n // an array, but this is current behavior)\n // Caveat: STRING (if valid) will pass successfully\n // bug give unexpected results\n // Info: Cannot use `== false to check falsey values`\n if (!!groups && groups.length !== 0) {\n // TODO: create a better understanding of expectations\n if (typeof groups === 'string') {\n throw new TypeError('plugin group error: invalid type for function');\n }\n const isGroupValid: boolean = Array.isArray(groups);\n if (!isGroupValid) {\n throw new TypeError(API_ERROR.BAD_FORMAT_USER_GROUP);\n }\n\n debug('authentication for user %o was successfully. Groups: %o', username, groups);\n return cb(err, createRemoteUser(username, groups));\n }\n next();\n });\n })();\n }\n\n public add_user(\n user: string,\n password: string,\n cb: (error: VerdaccioError | null, user?: RemoteUser) => void\n ): void {\n const self = this;\n const plugins = this.plugins.slice(0);\n debug('add user %o', user);\n\n (function next(): void {\n let method = 'adduser';\n const plugin = plugins.shift();\n // @ts-expect-error future major (7.x) should remove this section\n if (typeof plugin.adduser === 'undefined' && typeof plugin.add_user === 'function') {\n method = 'add_user';\n warningUtils.emit(warningUtils.Codes.VERWAR006);\n }\n // @ts-ignore\n if (typeof plugin[method] !== 'function') {\n next();\n } else {\n // TODO: replace by adduser whenever add_user deprecation method has been removed\n // @ts-ignore\n plugin[method](\n user,\n password,\n function (err: VerdaccioError | null, ok?: boolean | string): void {\n if (err) {\n debug('the user %o could not be added. Error: %o', user, err?.message);\n return cb(err);\n }\n if (ok) {\n debug('the user %o has been added', user);\n return self.authenticate(user, password, cb);\n }\n debug('user could not be added, skip to next auth plugin');\n next();\n }\n );\n }\n })();\n }\n\n /**\n * Allow user to access a package.\n */\n public allow_access(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: pluginUtils.AccessCallback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n ) as AllowAccess & PackageAccess;\n\n debug('check access permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_access !== 'function') {\n debug('plugin does not implement allow_access');\n return next();\n }\n\n plugin.allow_access(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden access. Error: %o', err);\n return callback(err);\n }\n\n if (ok) {\n debug('access was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `access was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('access was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate access denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n public allow_unpublish(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: Callback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n );\n\n debug('check unpublish permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_unpublish !== 'function') {\n debug('plugin does not implement allow_unpublish');\n return next();\n }\n\n plugin.allow_unpublish(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden unpublish. Error: %o', err);\n return callback(err);\n }\n\n // The following is different from the allow_access and allow_publish implementations:\n // If the packages config is missing an entry for \"unpublish\", the built-in default method\n // (or a plugin) will return undefined, which will trigger the allow_publish fallback.\n // (see utils.ts, handleSpecialUnpublish, callback(null, undefined))\n if (isNil(ok) === true) {\n debug('bypass unpublish for %o, publish will handle the access', packageName);\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `bypass unpublish for @{name} by @{user}, publish will handle the access`\n );\n return this.allow_publish({ packageName, packageVersion }, user, callback);\n }\n\n if (ok) {\n debug('unpublish was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `unpublish was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('unpublish was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate unpublish denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n /**\n * Allow user to publish a package.\n */\n public allow_publish(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: Callback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n );\n\n debug('check publish permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_publish !== 'function') {\n debug('plugin does not implement allow_publish');\n return next();\n }\n\n plugin.allow_publish(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden publish. Error: %o', err);\n return callback(err);\n }\n\n if (ok) {\n debug('publish was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `publish was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('publish was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate publish denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n public apiJWTmiddleware(): any {\n debug('jwt middleware');\n const plugins = this.plugins.slice(0);\n const helpers = { createAnonymousRemoteUser, createRemoteUser };\n for (const plugin of plugins) {\n if (plugin.apiJWTmiddleware) {\n return plugin.apiJWTmiddleware(helpers);\n }\n }\n\n return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction) => {\n req.pause();\n const next = function (err?: VerdaccioError): NextFunction {\n req.resume();\n if (err) {\n return _next(err) as unknown as NextFunction;\n }\n\n return _next() as unknown as NextFunction;\n };\n\n // FUTURE: disabled, not removed yet but seems unreacable code\n // if (this._isRemoteUserValid(req.remote_user)) {\n // debug('jwt has a valid authentication header');\n // return next();\n // }\n\n // in case auth header does not exist we return anonymous function\n const remoteUser = createAnonymousRemoteUser();\n req.remote_user = remoteUser;\n res.locals.remote_user = remoteUser;\n\n const { authorization } = req.headers;\n if (isNil(authorization)) {\n debug('jwt, authentication header is missing');\n return next();\n }\n\n if (!isAuthHeaderValid(authorization)) {\n debug('api middleware authentication heather is invalid');\n return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));\n }\n const { secret, security } = this.config;\n\n if (isAESLegacy(security)) {\n debug('api middleware using legacy auth token');\n this.handleAESMiddleware(req, security, secret, authorization, next);\n } else {\n debug('api middleware using JWT auth token');\n this.handleJWTAPIMiddleware(req, security, secret, authorization, next);\n }\n };\n }\n\n private handleJWTAPIMiddleware(\n req: $RequestExtend,\n security: Security,\n secret: string,\n authorization: string,\n next: any\n ): void {\n debug('handle JWT api middleware');\n const credentials: any = getMiddlewareCredentials(security, secret, authorization);\n if (credentials) {\n // if the signature is valid we rely on it\n req.remote_user = credentials;\n debug('generating a remote user');\n next();\n } else {\n // with JWT throw 401\n debug('jwt invalid token');\n next(errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));\n }\n }\n\n private handleAESMiddleware(\n req: $RequestExtend,\n security: Security,\n secret: string,\n authorization: string,\n next: Function\n ): void {\n debug('handle legacy api middleware');\n debug('api middleware has a secret? %o', typeof secret === 'string');\n debug('api middleware authorization %o', typeof authorization === 'string');\n const credentials: any = getMiddlewareCredentials(security, secret, authorization);\n debug('api middleware credentials %o', credentials?.name);\n if (credentials) {\n const cacheKey = this.getLegacyAuthCacheKey(authorization);\n const cachedUser = this.getLegacyAuthCacheEntry(cacheKey);\n if (cachedUser) {\n req.remote_user = cachedUser;\n debug('generating cached remote user');\n return next();\n }\n\n const { user, password } = credentials;\n const applyAuthResult = (err: VerdaccioError | null, user?: RemoteUser): void => {\n if (!err && user) {\n req.remote_user = credentials.tokenKey\n ? { ...user, token: { key: credentials.tokenKey } }\n : user;\n debug('generating a remote user');\n next();\n } else {\n req.remote_user = createAnonymousRemoteUser();\n debug('generating anonymous user');\n next(err || errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));\n }\n };\n // concurrent requests for the same token wait for the in-flight one\n if (this.enqueueLegacyAuthCacheWaiter(cacheKey, applyAuthResult)) {\n return;\n }\n\n debug('authenticating %o', user);\n const onAuthComplete = (err: VerdaccioError | null, user?: RemoteUser): void => {\n // only the leader writes the cache; waiters just reuse its result\n if (!err && user) {\n this.setLegacyAuthCacheEntry(\n cacheKey,\n credentials.tokenKey ? { ...user, token: { key: credentials.tokenKey } } : user\n );\n }\n applyAuthResult(err, user);\n this.resolveLegacyAuthCacheWaiters(cacheKey, err, user);\n };\n try {\n this.authenticate(user, password, onAuthComplete);\n } catch (err: any) {\n onAuthComplete(errorUtils.getInternalError(err?.message));\n }\n } else {\n const remoteUser = this.getJWTRemoteUserFromBearer(authorization);\n if (remoteUser) {\n req.remote_user = remoteUser;\n debug('generating a remote user from jwt bearer');\n return next();\n }\n\n debug('legacy invalid header');\n return next(errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));\n }\n }\n\n private getJWTRemoteUserFromBearer(authorization: string): RemoteUser | void {\n const { scheme, token } = parseAuthTokenHeader(authorization);\n if (scheme.toUpperCase() !== TOKEN_BEARER.toUpperCase() || !token) {\n return;\n }\n\n let credentials: RemoteUser | undefined;\n try {\n credentials = verifyJWTPayload(token, this.config.secret, this.config.security);\n } catch {\n return;\n }\n\n if (this._isRemoteUserValid(credentials)) {\n const { name, groups } = credentials as RemoteUser;\n return createRemoteUser(name as string, groups);\n }\n }\n\n private enqueueLegacyAuthCacheWaiter(\n cacheKey: string | void,\n waiter: LegacyAuthCacheWaiter\n ): boolean {\n if (!cacheKey) {\n return false;\n }\n\n const waiters = this.legacyAuthCacheWaiters.get(cacheKey);\n if (!waiters) {\n this.legacyAuthCacheWaiters.set(cacheKey, []);\n return false;\n }\n\n waiters.push(waiter);\n return true;\n }\n\n private resolveLegacyAuthCacheWaiters(\n cacheKey: string | void,\n err: VerdaccioError | null,\n user?: RemoteUser\n ): void {\n if (!cacheKey) {\n return;\n }\n\n const waiters = this.legacyAuthCacheWaiters.get(cacheKey);\n this.legacyAuthCacheWaiters.delete(cacheKey);\n if (!waiters) {\n return;\n }\n\n for (const waiter of waiters) {\n waiter(err, user);\n }\n }\n\n private isLegacyAuthCacheEnabled(): boolean {\n // opt-in: disabled unless explicitly turned on via config\n return this.config.server?.legacyAuthCache?.enabled === true;\n }\n\n private getLegacyAuthCacheTtlMs(): number {\n const ttlMs = this.config.server?.legacyAuthCache?.ttlMs;\n return typeof ttlMs === 'number' && ttlMs > 0 ? ttlMs : 30 * 1000;\n }\n\n private getLegacyAuthCacheMaxEntries(): number {\n const maxEntries = this.config.server?.legacyAuthCache?.maxEntries;\n return typeof maxEntries === 'number' && maxEntries > 0 ? maxEntries : 1000;\n }\n\n private getLegacyAuthCacheKey(authorization: string): string | void {\n if (!this.isLegacyAuthCacheEnabled()) {\n return;\n }\n\n const { scheme } = parseAuthTokenHeader(authorization);\n if (scheme.toUpperCase() !== TOKEN_BEARER.toUpperCase()) {\n return;\n }\n\n return createHash('sha256').update(authorization).digest('hex');\n }\n\n private getLegacyAuthCacheEntry(cacheKey: string | void): RemoteUser | void {\n if (!cacheKey) {\n return;\n }\n\n const entry = this.legacyAuthCache.get(cacheKey);\n if (!entry) {\n return;\n }\n\n if (entry.expiresAt <= Date.now()) {\n this.legacyAuthCache.delete(cacheKey);\n return;\n }\n\n this.legacyAuthCache.delete(cacheKey);\n this.legacyAuthCache.set(cacheKey, entry);\n return cloneRemoteUser(entry.user);\n }\n\n private setLegacyAuthCacheEntry(cacheKey: string | void, user: RemoteUser): void {\n if (!cacheKey) {\n return;\n }\n\n this.legacyAuthCache.set(cacheKey, {\n expiresAt: Date.now() + this.getLegacyAuthCacheTtlMs(),\n user: cloneRemoteUser(user),\n });\n\n const maxEntries = this.getLegacyAuthCacheMaxEntries();\n while (this.legacyAuthCache.size > maxEntries) {\n const oldestKey = this.legacyAuthCache.keys().next().value;\n if (!oldestKey) {\n break;\n }\n this.legacyAuthCache.delete(oldestKey);\n }\n }\n\n private _isRemoteUserValid(remote_user?: RemoteUser): boolean {\n return isUndefined(remote_user) === false && isUndefined(remote_user?.name) === false;\n }\n\n /**\n * JWT middleware for WebUI\n */\n public webUIJWTmiddleware() {\n return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction): void => {\n if (this._isRemoteUserValid(req.remote_user)) {\n return _next();\n }\n\n req.pause();\n const next = (err: VerdaccioError | void): void => {\n req.resume();\n if (err) {\n req.remote_user.error = err.message;\n res.status(err.statusCode).send(err.message);\n }\n\n return _next();\n };\n\n const { authorization } = req.headers;\n if (isNil(authorization)) {\n req.remote_user = createAnonymousRemoteUser();\n return next();\n }\n\n if (!isAuthHeaderValid(authorization)) {\n return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));\n }\n\n const token = (authorization || '').replace(`${TOKEN_BEARER} `, '');\n if (!token) {\n return next();\n }\n\n let credentials: RemoteUser | undefined;\n try {\n credentials = verifyJWTPayload(token, this.config.secret, this.config.security);\n } catch {\n // FIXME: intended behaviour, do we want it?\n }\n\n if (this._isRemoteUserValid(credentials)) {\n const { name, groups } = credentials as RemoteUser;\n req.remote_user = createRemoteUser(name as string, groups);\n } else {\n req.remote_user = createAnonymousRemoteUser();\n }\n\n next();\n };\n }\n\n public async jwtEncrypt(user: RemoteUser, signOptions: JWTSignOptions): Promise<string> {\n const { real_groups, name, groups, token: tokenMetadata } = user;\n debug('jwt encrypt %o', name);\n const realGroupsValidated = isNil(real_groups) ? [] : real_groups;\n const groupedGroups = isNil(groups)\n ? real_groups\n : Array.from(new Set([...groups.concat(realGroupsValidated)]));\n const payload: RemoteUser = {\n real_groups: realGroupsValidated,\n name,\n groups: groupedGroups,\n };\n if (tokenMetadata?.key) {\n payload.token = { key: tokenMetadata.key };\n }\n const signedToken: string = await signPayload(\n payload,\n this.secret,\n signOptions as Parameters<typeof signPayload>[2]\n );\n\n return signedToken;\n }\n\n /**\n * Encrypt a string.\n */\n public aesEncrypt(value: string): string | void {\n debug('signing with aes encryption');\n const token = aesEncrypt(value, this.secret);\n return token;\n }\n}\n\nexport { Auth };\n"],"mappings":"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"}
|
package/build/auth.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getDefaultPluginMethods, getMiddlewareCredentials, isAESLegacy, isAuthHeaderValid, parseAuthTokenHeader, verifyJWTPayload } from "./utils.mjs";
|
|
2
2
|
import buildDebug from "debug";
|
|
3
3
|
import { filter, isEmpty, isNil, isUndefined } from "lodash-es";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { HTPasswd } from "verdaccio-htpasswd";
|
|
6
6
|
import { createAnonymousRemoteUser, createRemoteUser } from "@verdaccio/config";
|
|
7
|
-
import { API_ERROR, PLUGIN_CATEGORY, PLUGIN_PREFIX, SUPPORT_ERRORS,
|
|
7
|
+
import { API_ERROR, PLUGIN_CATEGORY, PLUGIN_PREFIX, SUPPORT_ERRORS, TOKEN_BEARER, authUtils, errorUtils, pluginUtils, warningUtils } from "@verdaccio/core";
|
|
8
8
|
import { asyncLoadPlugin } from "@verdaccio/loaders";
|
|
9
|
-
import { aesEncrypt,
|
|
9
|
+
import { aesEncrypt, signPayload } from "@verdaccio/signature";
|
|
10
10
|
//#region src/auth.ts
|
|
11
11
|
var debug = buildDebug("verdaccio:auth");
|
|
12
12
|
function cloneRemoteUser(user) {
|
|
@@ -280,7 +280,7 @@ var Auth = class {
|
|
|
280
280
|
req.pause();
|
|
281
281
|
const next = function(err) {
|
|
282
282
|
req.resume();
|
|
283
|
-
if (err)
|
|
283
|
+
if (err) return _next(err);
|
|
284
284
|
return _next();
|
|
285
285
|
};
|
|
286
286
|
const remoteUser = createAnonymousRemoteUser();
|
|
@@ -307,40 +307,14 @@ var Auth = class {
|
|
|
307
307
|
}
|
|
308
308
|
handleJWTAPIMiddleware(req, security, secret, authorization, next) {
|
|
309
309
|
debug("handle JWT api middleware");
|
|
310
|
-
const
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (!parsedCredentials) {
|
|
316
|
-
debug("invalid basic credentials format (missing colon separator)");
|
|
317
|
-
next(errorUtils.getBadRequest(API_ERROR.BAD_USERNAME_PASSWORD));
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
const { user, password } = parsedCredentials;
|
|
321
|
-
debug("authenticating %o", user);
|
|
322
|
-
this.authenticate(user, password, (err, user) => {
|
|
323
|
-
if (!err) {
|
|
324
|
-
debug("generating a remote user");
|
|
325
|
-
req.remote_user = user;
|
|
326
|
-
next();
|
|
327
|
-
} else {
|
|
328
|
-
debug("generating anonymous user");
|
|
329
|
-
req.remote_user = createAnonymousRemoteUser();
|
|
330
|
-
next(err);
|
|
331
|
-
}
|
|
332
|
-
});
|
|
310
|
+
const credentials = getMiddlewareCredentials(security, secret, authorization);
|
|
311
|
+
if (credentials) {
|
|
312
|
+
req.remote_user = credentials;
|
|
313
|
+
debug("generating a remote user");
|
|
314
|
+
next();
|
|
333
315
|
} else {
|
|
334
|
-
debug("
|
|
335
|
-
|
|
336
|
-
if (credentials) {
|
|
337
|
-
req.remote_user = credentials;
|
|
338
|
-
debug("generating a remote user");
|
|
339
|
-
next();
|
|
340
|
-
} else {
|
|
341
|
-
debug("jwt invalid token");
|
|
342
|
-
next(errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));
|
|
343
|
-
}
|
|
316
|
+
debug("jwt invalid token");
|
|
317
|
+
next(errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
|
|
344
318
|
}
|
|
345
319
|
}
|
|
346
320
|
handleAESMiddleware(req, security, secret, authorization, next) {
|
|
@@ -369,7 +343,7 @@ var Auth = class {
|
|
|
369
343
|
} else {
|
|
370
344
|
req.remote_user = createAnonymousRemoteUser();
|
|
371
345
|
debug("generating anonymous user");
|
|
372
|
-
next(err || errorUtils.
|
|
346
|
+
next(err || errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
|
|
373
347
|
}
|
|
374
348
|
};
|
|
375
349
|
if (this.enqueueLegacyAuthCacheWaiter(cacheKey, applyAuthResult)) return;
|
|
@@ -388,8 +362,28 @@ var Auth = class {
|
|
|
388
362
|
onAuthComplete(errorUtils.getInternalError(err?.message));
|
|
389
363
|
}
|
|
390
364
|
} else {
|
|
365
|
+
const remoteUser = this.getJWTRemoteUserFromBearer(authorization);
|
|
366
|
+
if (remoteUser) {
|
|
367
|
+
req.remote_user = remoteUser;
|
|
368
|
+
debug("generating a remote user from jwt bearer");
|
|
369
|
+
return next();
|
|
370
|
+
}
|
|
391
371
|
debug("legacy invalid header");
|
|
392
|
-
return next(errorUtils.
|
|
372
|
+
return next(errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
getJWTRemoteUserFromBearer(authorization) {
|
|
376
|
+
const { scheme, token } = parseAuthTokenHeader(authorization);
|
|
377
|
+
if (scheme.toUpperCase() !== TOKEN_BEARER.toUpperCase() || !token) return;
|
|
378
|
+
let credentials;
|
|
379
|
+
try {
|
|
380
|
+
credentials = verifyJWTPayload(token, this.config.secret, this.config.security);
|
|
381
|
+
} catch {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (this._isRemoteUserValid(credentials)) {
|
|
385
|
+
const { name, groups } = credentials;
|
|
386
|
+
return createRemoteUser(name, groups);
|
|
393
387
|
}
|
|
394
388
|
}
|
|
395
389
|
enqueueLegacyAuthCacheWaiter(cacheKey, waiter) {
|
|
@@ -470,7 +464,10 @@ var Auth = class {
|
|
|
470
464
|
return _next();
|
|
471
465
|
};
|
|
472
466
|
const { authorization } = req.headers;
|
|
473
|
-
if (isNil(authorization))
|
|
467
|
+
if (isNil(authorization)) {
|
|
468
|
+
req.remote_user = createAnonymousRemoteUser();
|
|
469
|
+
return next();
|
|
470
|
+
}
|
|
474
471
|
if (!isAuthHeaderValid(authorization)) return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));
|
|
475
472
|
const token = (authorization || "").replace(`${TOKEN_BEARER} `, "");
|
|
476
473
|
if (!token) return next();
|
package/build/auth.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.mjs","names":[],"sources":["../src/auth.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { filter, isEmpty, isNil, isUndefined } from 'lodash-es';\nimport { createHash } from 'node:crypto';\nimport { HTPasswd } from 'verdaccio-htpasswd';\n\nimport { createAnonymousRemoteUser, createRemoteUser } from '@verdaccio/config';\nimport type { VerdaccioError, pluginUtils } from '@verdaccio/core';\nimport {\n API_ERROR,\n PLUGIN_CATEGORY,\n PLUGIN_PREFIX,\n SUPPORT_ERRORS,\n TOKEN_BASIC,\n TOKEN_BEARER,\n authUtils,\n errorUtils,\n pluginUtils as pluginSanity,\n warningUtils,\n} from '@verdaccio/core';\nimport { asyncLoadPlugin } from '@verdaccio/loaders';\nimport { aesEncrypt, parseBasicPayload, signPayload } from '@verdaccio/signature';\nimport type {\n AllowAccess,\n Callback,\n Config,\n JWTSignOptions,\n Logger,\n PackageAccess,\n RemoteUser,\n Security,\n} from '@verdaccio/types';\n\nimport type {\n $RequestExtend,\n $ResponseExtend,\n AESPayload,\n IAuthMiddleware,\n NextFunction,\n TokenEncryption,\n} from './types';\nimport {\n convertPayloadToBase64,\n getDefaultPluginMethods,\n getMiddlewareCredentials,\n isAESLegacy,\n isAuthHeaderValid,\n parseAuthTokenHeader,\n verifyJWTPayload,\n} from './utils';\n\nconst debug = buildDebug('verdaccio:auth');\ntype LegacyAuthCacheEntry = {\n expiresAt: number;\n user: RemoteUser;\n};\n\ntype LegacyAuthCacheWaiter = (err: VerdaccioError | null, user?: RemoteUser) => void;\n\nfunction cloneRemoteUser(user: RemoteUser): RemoteUser {\n return {\n ...user,\n groups: user.groups ? [...user.groups] : user.groups,\n real_groups: user.real_groups ? [...user.real_groups] : user.real_groups,\n token: user.token ? { ...user.token } : user.token,\n };\n}\n\nclass Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {\n public config: Config;\n public secret: string;\n public logger: Logger;\n public plugins: pluginUtils.Auth<Config>[];\n public options: { legacyMergeConfigs: boolean };\n private legacyAuthCache: Map<string, LegacyAuthCacheEntry>;\n private legacyAuthCacheWaiters: Map<string, LegacyAuthCacheWaiter[]>;\n\n public constructor(config: Config, logger: Logger, options = { legacyMergeConfigs: false }) {\n this.config = config;\n this.secret = config.secret;\n this.logger = logger;\n this.plugins = [];\n this.options = options;\n this.legacyAuthCache = new Map();\n this.legacyAuthCacheWaiters = new Map();\n if (!this.secret) {\n throw new TypeError('secret it is required value on initialize the auth class');\n }\n }\n\n public async init() {\n let plugins = await this.loadPlugin();\n\n debug('auth plugins found %s', plugins.length);\n // Missing auth config or no loaded plugins -> load default htpasswd plugin\n // Empty auth config (null) -> just use fallback methods\n if (this.config.auth !== null && (!plugins || plugins.length === 0)) {\n plugins = this.loadDefaultPlugin();\n }\n this.plugins = plugins;\n\n this.applyFallbackPluginMethods();\n }\n\n private loadDefaultPlugin() {\n debug('load default auth plugin');\n let authPlugin;\n try {\n authPlugin = new HTPasswd(\n { file: './htpasswd' },\n {\n config: this.config,\n logger: this.logger,\n }\n );\n this.logger.info(\n { name: 'verdaccio-htpasswd', pluginCategory: PLUGIN_CATEGORY.AUTHENTICATION },\n 'plugin @{name} successfully loaded (@{pluginCategory})'\n );\n } catch (error: any) {\n debug('error on loading auth htpasswd plugin stack: %o', error);\n this.logger.info({}, 'no auth plugin has been found');\n return [];\n }\n\n return [authPlugin];\n }\n\n private async loadPlugin() {\n return asyncLoadPlugin<pluginUtils.Auth<Config>>(\n this.config.auth,\n {\n config: this.config,\n logger: this.logger,\n },\n pluginSanity.authSanityCheck,\n this.options.legacyMergeConfigs,\n this.config?.server?.pluginPrefix ?? PLUGIN_PREFIX,\n PLUGIN_CATEGORY.AUTHENTICATION\n );\n }\n\n private applyFallbackPluginMethods(): void {\n this.plugins.push(getDefaultPluginMethods(this.logger));\n }\n\n public changePassword(\n username: string,\n password: string,\n newPassword: string,\n cb: Callback\n ): void {\n const validPlugins = filter(\n this.plugins,\n (plugin) => typeof plugin.changePassword === 'function'\n );\n\n if (isEmpty(validPlugins)) {\n return cb(errorUtils.getInternalError(SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));\n }\n\n for (const plugin of validPlugins) {\n if (isNil(plugin) || typeof plugin.changePassword !== 'function') {\n debug('auth plugin does not implement changePassword, trying next one');\n continue;\n } else {\n debug('updating password for %o', username);\n plugin.changePassword!(username, password, newPassword, (err, profile): void => {\n if (err) {\n this.logger.error(\n { username, err },\n `An error has been produced\n updating the password for @{username}. Error: @{err.message}`\n );\n return cb(err);\n }\n\n debug('updated password for %o was successful', username);\n return cb(null, profile);\n });\n }\n }\n }\n\n public async invalidateToken(token: string) {\n // eslint-disable-next-line no-console\n console.log('invalidate token pending to implement', token);\n return Promise.resolve();\n }\n\n public authenticate(\n username: string,\n password: string,\n cb: (error: VerdaccioError | null, user?: RemoteUser) => void\n ): void {\n const plugins = this.plugins.slice(0);\n (function next(): void {\n const plugin = plugins.shift();\n\n if (typeof plugin?.authenticate !== 'function') {\n return next();\n }\n\n debug('authenticating %o', username);\n plugin.authenticate(username, password, function (err: VerdaccioError | null, groups): void {\n if (err) {\n debug('authenticating for user %o failed. Error: %o', username, err?.message);\n return cb(err);\n }\n\n // Expect: SKIP if groups is falsey and not an array\n // with at least one item (truthy length)\n // Expect: CONTINUE otherwise (will error if groups is not\n // an array, but this is current behavior)\n // Caveat: STRING (if valid) will pass successfully\n // bug give unexpected results\n // Info: Cannot use `== false to check falsey values`\n if (!!groups && groups.length !== 0) {\n // TODO: create a better understanding of expectations\n if (typeof groups === 'string') {\n throw new TypeError('plugin group error: invalid type for function');\n }\n const isGroupValid: boolean = Array.isArray(groups);\n if (!isGroupValid) {\n throw new TypeError(API_ERROR.BAD_FORMAT_USER_GROUP);\n }\n\n debug('authentication for user %o was successfully. Groups: %o', username, groups);\n return cb(err, createRemoteUser(username, groups));\n }\n next();\n });\n })();\n }\n\n public add_user(\n user: string,\n password: string,\n cb: (error: VerdaccioError | null, user?: RemoteUser) => void\n ): void {\n const self = this;\n const plugins = this.plugins.slice(0);\n debug('add user %o', user);\n\n (function next(): void {\n let method = 'adduser';\n const plugin = plugins.shift();\n // @ts-expect-error future major (7.x) should remove this section\n if (typeof plugin.adduser === 'undefined' && typeof plugin.add_user === 'function') {\n method = 'add_user';\n warningUtils.emit(warningUtils.Codes.VERWAR006);\n }\n // @ts-ignore\n if (typeof plugin[method] !== 'function') {\n next();\n } else {\n // TODO: replace by adduser whenever add_user deprecation method has been removed\n // @ts-ignore\n plugin[method](\n user,\n password,\n function (err: VerdaccioError | null, ok?: boolean | string): void {\n if (err) {\n debug('the user %o could not be added. Error: %o', user, err?.message);\n return cb(err);\n }\n if (ok) {\n debug('the user %o has been added', user);\n return self.authenticate(user, password, cb);\n }\n debug('user could not be added, skip to next auth plugin');\n next();\n }\n );\n }\n })();\n }\n\n /**\n * Allow user to access a package.\n */\n public allow_access(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: pluginUtils.AccessCallback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n ) as AllowAccess & PackageAccess;\n\n debug('check access permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_access !== 'function') {\n debug('plugin does not implement allow_access');\n return next();\n }\n\n plugin.allow_access(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden access. Error: %o', err);\n return callback(err);\n }\n\n if (ok) {\n debug('access was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `access was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('access was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate access denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n public allow_unpublish(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: Callback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n );\n\n debug('check unpublish permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_unpublish !== 'function') {\n debug('plugin does not implement allow_unpublish');\n return next();\n }\n\n plugin.allow_unpublish(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden unpublish. Error: %o', err);\n return callback(err);\n }\n\n // The following is different from the allow_access and allow_publish implementations:\n // If the packages config is missing an entry for \"unpublish\", the built-in default method\n // (or a plugin) will return undefined, which will trigger the allow_publish fallback.\n // (see utils.ts, handleSpecialUnpublish, callback(null, undefined))\n if (isNil(ok) === true) {\n debug('bypass unpublish for %o, publish will handle the access', packageName);\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `bypass unpublish for @{name} by @{user}, publish will handle the access`\n );\n return this.allow_publish({ packageName, packageVersion }, user, callback);\n }\n\n if (ok) {\n debug('unpublish was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `unpublish was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('unpublish was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate unpublish denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n /**\n * Allow user to publish a package.\n */\n public allow_publish(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: Callback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n );\n\n debug('check publish permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_publish !== 'function') {\n debug('plugin does not implement allow_publish');\n return next();\n }\n\n plugin.allow_publish(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden publish. Error: %o', err);\n return callback(err);\n }\n\n if (ok) {\n debug('publish was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `publish was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('publish was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate publish denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n public apiJWTmiddleware(): any {\n debug('jwt middleware');\n const plugins = this.plugins.slice(0);\n const helpers = { createAnonymousRemoteUser, createRemoteUser };\n for (const plugin of plugins) {\n if (plugin.apiJWTmiddleware) {\n return plugin.apiJWTmiddleware(helpers);\n }\n }\n\n return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction) => {\n req.pause();\n const next = function (err?: VerdaccioError): NextFunction {\n req.resume();\n // uncomment this to reject users with bad auth headers\n // return _next.apply(null, arguments)\n // swallow error, user remains unauthorized\n // set remoteUserError to indicate that user was attempting authentication\n if (err) {\n req.remote_user.error = err.message;\n }\n\n return _next() as unknown as NextFunction;\n };\n\n // FUTURE: disabled, not removed yet but seems unreacable code\n // if (this._isRemoteUserValid(req.remote_user)) {\n // debug('jwt has a valid authentication header');\n // return next();\n // }\n\n // in case auth header does not exist we return anonymous function\n const remoteUser = createAnonymousRemoteUser();\n req.remote_user = remoteUser;\n res.locals.remote_user = remoteUser;\n\n const { authorization } = req.headers;\n if (isNil(authorization)) {\n debug('jwt, authentication header is missing');\n return next();\n }\n\n if (!isAuthHeaderValid(authorization)) {\n debug('api middleware authentication heather is invalid');\n return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));\n }\n const { secret, security } = this.config;\n\n if (isAESLegacy(security)) {\n debug('api middleware using legacy auth token');\n this.handleAESMiddleware(req, security, secret, authorization, next);\n } else {\n debug('api middleware using JWT auth token');\n this.handleJWTAPIMiddleware(req, security, secret, authorization, next);\n }\n };\n }\n\n private handleJWTAPIMiddleware(\n req: $RequestExtend,\n security: Security,\n secret: string,\n authorization: string,\n next: any\n ): void {\n debug('handle JWT api middleware');\n const { scheme, token } = parseAuthTokenHeader(authorization);\n if (scheme.toUpperCase() === TOKEN_BASIC.toUpperCase()) {\n debug('handle basic token');\n // this should happen when client tries to login with an existing user\n const credentials = convertPayloadToBase64(token).toString();\n const parsedCredentials = parseBasicPayload(credentials);\n if (!parsedCredentials) {\n debug('invalid basic credentials format (missing colon separator)');\n next(errorUtils.getBadRequest(API_ERROR.BAD_USERNAME_PASSWORD));\n return;\n }\n const { user, password } = parsedCredentials as AESPayload;\n debug('authenticating %o', user);\n this.authenticate(user, password, (err: VerdaccioError | null, user): void => {\n if (!err) {\n debug('generating a remote user');\n req.remote_user = user;\n next();\n } else {\n debug('generating anonymous user');\n req.remote_user = createAnonymousRemoteUser();\n next(err);\n }\n });\n } else {\n debug('handle jwt token');\n const credentials: any = getMiddlewareCredentials(security, secret, authorization);\n if (credentials) {\n // if the signature is valid we rely on it\n req.remote_user = credentials;\n debug('generating a remote user');\n next();\n } else {\n // with JWT throw 401\n debug('jwt invalid token');\n next(errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));\n }\n }\n }\n\n private handleAESMiddleware(\n req: $RequestExtend,\n security: Security,\n secret: string,\n authorization: string,\n next: Function\n ): void {\n debug('handle legacy api middleware');\n debug('api middleware has a secret? %o', typeof secret === 'string');\n debug('api middleware authorization %o', typeof authorization === 'string');\n const credentials: any = getMiddlewareCredentials(security, secret, authorization);\n debug('api middleware credentials %o', credentials?.name);\n if (credentials) {\n const cacheKey = this.getLegacyAuthCacheKey(authorization);\n const cachedUser = this.getLegacyAuthCacheEntry(cacheKey);\n if (cachedUser) {\n req.remote_user = cachedUser;\n debug('generating cached remote user');\n return next();\n }\n\n const { user, password } = credentials;\n const applyAuthResult = (err: VerdaccioError | null, user?: RemoteUser): void => {\n if (!err && user) {\n req.remote_user = credentials.tokenKey\n ? { ...user, token: { key: credentials.tokenKey } }\n : user;\n debug('generating a remote user');\n next();\n } else {\n req.remote_user = createAnonymousRemoteUser();\n debug('generating anonymous user');\n next(err || errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));\n }\n };\n // concurrent requests for the same token wait for the in-flight one\n if (this.enqueueLegacyAuthCacheWaiter(cacheKey, applyAuthResult)) {\n return;\n }\n\n debug('authenticating %o', user);\n const onAuthComplete = (err: VerdaccioError | null, user?: RemoteUser): void => {\n // only the leader writes the cache; waiters just reuse its result\n if (!err && user) {\n this.setLegacyAuthCacheEntry(\n cacheKey,\n credentials.tokenKey ? { ...user, token: { key: credentials.tokenKey } } : user\n );\n }\n applyAuthResult(err, user);\n this.resolveLegacyAuthCacheWaiters(cacheKey, err, user);\n };\n try {\n this.authenticate(user, password, onAuthComplete);\n } catch (err: any) {\n onAuthComplete(errorUtils.getInternalError(err?.message));\n }\n } else {\n // we force npm client to ask again with basic authentication\n debug('legacy invalid header');\n return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));\n }\n }\n\n private enqueueLegacyAuthCacheWaiter(\n cacheKey: string | void,\n waiter: LegacyAuthCacheWaiter\n ): boolean {\n if (!cacheKey) {\n return false;\n }\n\n const waiters = this.legacyAuthCacheWaiters.get(cacheKey);\n if (!waiters) {\n this.legacyAuthCacheWaiters.set(cacheKey, []);\n return false;\n }\n\n waiters.push(waiter);\n return true;\n }\n\n private resolveLegacyAuthCacheWaiters(\n cacheKey: string | void,\n err: VerdaccioError | null,\n user?: RemoteUser\n ): void {\n if (!cacheKey) {\n return;\n }\n\n const waiters = this.legacyAuthCacheWaiters.get(cacheKey);\n this.legacyAuthCacheWaiters.delete(cacheKey);\n if (!waiters) {\n return;\n }\n\n for (const waiter of waiters) {\n waiter(err, user);\n }\n }\n\n private isLegacyAuthCacheEnabled(): boolean {\n // opt-in: disabled unless explicitly turned on via config\n return this.config.server?.legacyAuthCache?.enabled === true;\n }\n\n private getLegacyAuthCacheTtlMs(): number {\n const ttlMs = this.config.server?.legacyAuthCache?.ttlMs;\n return typeof ttlMs === 'number' && ttlMs > 0 ? ttlMs : 30 * 1000;\n }\n\n private getLegacyAuthCacheMaxEntries(): number {\n const maxEntries = this.config.server?.legacyAuthCache?.maxEntries;\n return typeof maxEntries === 'number' && maxEntries > 0 ? maxEntries : 1000;\n }\n\n private getLegacyAuthCacheKey(authorization: string): string | void {\n if (!this.isLegacyAuthCacheEnabled()) {\n return;\n }\n\n const { scheme } = parseAuthTokenHeader(authorization);\n if (scheme.toUpperCase() !== TOKEN_BEARER.toUpperCase()) {\n return;\n }\n\n return createHash('sha256').update(authorization).digest('hex');\n }\n\n private getLegacyAuthCacheEntry(cacheKey: string | void): RemoteUser | void {\n if (!cacheKey) {\n return;\n }\n\n const entry = this.legacyAuthCache.get(cacheKey);\n if (!entry) {\n return;\n }\n\n if (entry.expiresAt <= Date.now()) {\n this.legacyAuthCache.delete(cacheKey);\n return;\n }\n\n this.legacyAuthCache.delete(cacheKey);\n this.legacyAuthCache.set(cacheKey, entry);\n return cloneRemoteUser(entry.user);\n }\n\n private setLegacyAuthCacheEntry(cacheKey: string | void, user: RemoteUser): void {\n if (!cacheKey) {\n return;\n }\n\n this.legacyAuthCache.set(cacheKey, {\n expiresAt: Date.now() + this.getLegacyAuthCacheTtlMs(),\n user: cloneRemoteUser(user),\n });\n\n const maxEntries = this.getLegacyAuthCacheMaxEntries();\n while (this.legacyAuthCache.size > maxEntries) {\n const oldestKey = this.legacyAuthCache.keys().next().value;\n if (!oldestKey) {\n break;\n }\n this.legacyAuthCache.delete(oldestKey);\n }\n }\n\n private _isRemoteUserValid(remote_user?: RemoteUser): boolean {\n return isUndefined(remote_user) === false && isUndefined(remote_user?.name) === false;\n }\n\n /**\n * JWT middleware for WebUI\n */\n public webUIJWTmiddleware() {\n return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction): void => {\n if (this._isRemoteUserValid(req.remote_user)) {\n return _next();\n }\n\n req.pause();\n const next = (err: VerdaccioError | void): void => {\n req.resume();\n if (err) {\n req.remote_user.error = err.message;\n res.status(err.statusCode).send(err.message);\n }\n\n return _next();\n };\n\n const { authorization } = req.headers;\n if (isNil(authorization)) {\n return next();\n }\n\n if (!isAuthHeaderValid(authorization)) {\n return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));\n }\n\n const token = (authorization || '').replace(`${TOKEN_BEARER} `, '');\n if (!token) {\n return next();\n }\n\n let credentials: RemoteUser | undefined;\n try {\n credentials = verifyJWTPayload(token, this.config.secret, this.config.security);\n } catch {\n // FIXME: intended behaviour, do we want it?\n }\n\n if (this._isRemoteUserValid(credentials)) {\n const { name, groups } = credentials as RemoteUser;\n req.remote_user = createRemoteUser(name as string, groups);\n } else {\n req.remote_user = createAnonymousRemoteUser();\n }\n\n next();\n };\n }\n\n public async jwtEncrypt(user: RemoteUser, signOptions: JWTSignOptions): Promise<string> {\n const { real_groups, name, groups, token: tokenMetadata } = user;\n debug('jwt encrypt %o', name);\n const realGroupsValidated = isNil(real_groups) ? [] : real_groups;\n const groupedGroups = isNil(groups)\n ? real_groups\n : Array.from(new Set([...groups.concat(realGroupsValidated)]));\n const payload: RemoteUser = {\n real_groups: realGroupsValidated,\n name,\n groups: groupedGroups,\n };\n if (tokenMetadata?.key) {\n payload.token = { key: tokenMetadata.key };\n }\n const signedToken: string = await signPayload(\n payload,\n this.secret,\n signOptions as Parameters<typeof signPayload>[2]\n );\n\n return signedToken;\n }\n\n /**\n * Encrypt a string.\n */\n public aesEncrypt(value: string): string | void {\n debug('signing with aes encryption');\n const token = aesEncrypt(value, this.secret);\n return token;\n }\n}\n\nexport { Auth };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"auth.mjs","names":[],"sources":["../src/auth.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { filter, isEmpty, isNil, isUndefined } from 'lodash-es';\nimport { createHash } from 'node:crypto';\nimport { HTPasswd } from 'verdaccio-htpasswd';\n\nimport { createAnonymousRemoteUser, createRemoteUser } from '@verdaccio/config';\nimport type { VerdaccioError, pluginUtils } from '@verdaccio/core';\nimport {\n API_ERROR,\n PLUGIN_CATEGORY,\n PLUGIN_PREFIX,\n SUPPORT_ERRORS,\n TOKEN_BEARER,\n authUtils,\n errorUtils,\n pluginUtils as pluginSanity,\n warningUtils,\n} from '@verdaccio/core';\nimport { asyncLoadPlugin } from '@verdaccio/loaders';\nimport { aesEncrypt, signPayload } from '@verdaccio/signature';\nimport type {\n AllowAccess,\n Callback,\n Config,\n JWTSignOptions,\n Logger,\n PackageAccess,\n RemoteUser,\n Security,\n} from '@verdaccio/types';\n\nimport type {\n $RequestExtend,\n $ResponseExtend,\n IAuthMiddleware,\n NextFunction,\n TokenEncryption,\n} from './types';\nimport {\n getDefaultPluginMethods,\n getMiddlewareCredentials,\n isAESLegacy,\n isAuthHeaderValid,\n parseAuthTokenHeader,\n verifyJWTPayload,\n} from './utils';\n\nconst debug = buildDebug('verdaccio:auth');\ntype LegacyAuthCacheEntry = {\n expiresAt: number;\n user: RemoteUser;\n};\n\ntype LegacyAuthCacheWaiter = (err: VerdaccioError | null, user?: RemoteUser) => void;\n\nfunction cloneRemoteUser(user: RemoteUser): RemoteUser {\n return {\n ...user,\n groups: user.groups ? [...user.groups] : user.groups,\n real_groups: user.real_groups ? [...user.real_groups] : user.real_groups,\n token: user.token ? { ...user.token } : user.token,\n };\n}\n\nclass Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {\n public config: Config;\n public secret: string;\n public logger: Logger;\n public plugins: pluginUtils.Auth<Config>[];\n public options: { legacyMergeConfigs: boolean };\n private legacyAuthCache: Map<string, LegacyAuthCacheEntry>;\n private legacyAuthCacheWaiters: Map<string, LegacyAuthCacheWaiter[]>;\n\n public constructor(config: Config, logger: Logger, options = { legacyMergeConfigs: false }) {\n this.config = config;\n this.secret = config.secret;\n this.logger = logger;\n this.plugins = [];\n this.options = options;\n this.legacyAuthCache = new Map();\n this.legacyAuthCacheWaiters = new Map();\n if (!this.secret) {\n throw new TypeError('secret it is required value on initialize the auth class');\n }\n }\n\n public async init() {\n let plugins = await this.loadPlugin();\n\n debug('auth plugins found %s', plugins.length);\n // Missing auth config or no loaded plugins -> load default htpasswd plugin\n // Empty auth config (null) -> just use fallback methods\n if (this.config.auth !== null && (!plugins || plugins.length === 0)) {\n plugins = this.loadDefaultPlugin();\n }\n this.plugins = plugins;\n\n this.applyFallbackPluginMethods();\n }\n\n private loadDefaultPlugin() {\n debug('load default auth plugin');\n let authPlugin;\n try {\n authPlugin = new HTPasswd(\n { file: './htpasswd' },\n {\n config: this.config,\n logger: this.logger,\n }\n );\n this.logger.info(\n { name: 'verdaccio-htpasswd', pluginCategory: PLUGIN_CATEGORY.AUTHENTICATION },\n 'plugin @{name} successfully loaded (@{pluginCategory})'\n );\n } catch (error: any) {\n debug('error on loading auth htpasswd plugin stack: %o', error);\n this.logger.info({}, 'no auth plugin has been found');\n return [];\n }\n\n return [authPlugin];\n }\n\n private async loadPlugin() {\n return asyncLoadPlugin<pluginUtils.Auth<Config>>(\n this.config.auth,\n {\n config: this.config,\n logger: this.logger,\n },\n pluginSanity.authSanityCheck,\n this.options.legacyMergeConfigs,\n this.config?.server?.pluginPrefix ?? PLUGIN_PREFIX,\n PLUGIN_CATEGORY.AUTHENTICATION\n );\n }\n\n private applyFallbackPluginMethods(): void {\n this.plugins.push(getDefaultPluginMethods(this.logger));\n }\n\n public changePassword(\n username: string,\n password: string,\n newPassword: string,\n cb: Callback\n ): void {\n const validPlugins = filter(\n this.plugins,\n (plugin) => typeof plugin.changePassword === 'function'\n );\n\n if (isEmpty(validPlugins)) {\n return cb(errorUtils.getInternalError(SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));\n }\n\n for (const plugin of validPlugins) {\n if (isNil(plugin) || typeof plugin.changePassword !== 'function') {\n debug('auth plugin does not implement changePassword, trying next one');\n continue;\n } else {\n debug('updating password for %o', username);\n plugin.changePassword!(username, password, newPassword, (err, profile): void => {\n if (err) {\n this.logger.error(\n { username, err },\n `An error has been produced\n updating the password for @{username}. Error: @{err.message}`\n );\n return cb(err);\n }\n\n debug('updated password for %o was successful', username);\n return cb(null, profile);\n });\n }\n }\n }\n\n public async invalidateToken(token: string) {\n // eslint-disable-next-line no-console\n console.log('invalidate token pending to implement', token);\n return Promise.resolve();\n }\n\n public authenticate(\n username: string,\n password: string,\n cb: (error: VerdaccioError | null, user?: RemoteUser) => void\n ): void {\n const plugins = this.plugins.slice(0);\n (function next(): void {\n const plugin = plugins.shift();\n\n if (typeof plugin?.authenticate !== 'function') {\n return next();\n }\n\n debug('authenticating %o', username);\n plugin.authenticate(username, password, function (err: VerdaccioError | null, groups): void {\n if (err) {\n debug('authenticating for user %o failed. Error: %o', username, err?.message);\n return cb(err);\n }\n\n // Expect: SKIP if groups is falsey and not an array\n // with at least one item (truthy length)\n // Expect: CONTINUE otherwise (will error if groups is not\n // an array, but this is current behavior)\n // Caveat: STRING (if valid) will pass successfully\n // bug give unexpected results\n // Info: Cannot use `== false to check falsey values`\n if (!!groups && groups.length !== 0) {\n // TODO: create a better understanding of expectations\n if (typeof groups === 'string') {\n throw new TypeError('plugin group error: invalid type for function');\n }\n const isGroupValid: boolean = Array.isArray(groups);\n if (!isGroupValid) {\n throw new TypeError(API_ERROR.BAD_FORMAT_USER_GROUP);\n }\n\n debug('authentication for user %o was successfully. Groups: %o', username, groups);\n return cb(err, createRemoteUser(username, groups));\n }\n next();\n });\n })();\n }\n\n public add_user(\n user: string,\n password: string,\n cb: (error: VerdaccioError | null, user?: RemoteUser) => void\n ): void {\n const self = this;\n const plugins = this.plugins.slice(0);\n debug('add user %o', user);\n\n (function next(): void {\n let method = 'adduser';\n const plugin = plugins.shift();\n // @ts-expect-error future major (7.x) should remove this section\n if (typeof plugin.adduser === 'undefined' && typeof plugin.add_user === 'function') {\n method = 'add_user';\n warningUtils.emit(warningUtils.Codes.VERWAR006);\n }\n // @ts-ignore\n if (typeof plugin[method] !== 'function') {\n next();\n } else {\n // TODO: replace by adduser whenever add_user deprecation method has been removed\n // @ts-ignore\n plugin[method](\n user,\n password,\n function (err: VerdaccioError | null, ok?: boolean | string): void {\n if (err) {\n debug('the user %o could not be added. Error: %o', user, err?.message);\n return cb(err);\n }\n if (ok) {\n debug('the user %o has been added', user);\n return self.authenticate(user, password, cb);\n }\n debug('user could not be added, skip to next auth plugin');\n next();\n }\n );\n }\n })();\n }\n\n /**\n * Allow user to access a package.\n */\n public allow_access(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: pluginUtils.AccessCallback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n ) as AllowAccess & PackageAccess;\n\n debug('check access permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_access !== 'function') {\n debug('plugin does not implement allow_access');\n return next();\n }\n\n plugin.allow_access(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden access. Error: %o', err);\n return callback(err);\n }\n\n if (ok) {\n debug('access was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `access was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('access was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate access denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n public allow_unpublish(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: Callback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n );\n\n debug('check unpublish permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_unpublish !== 'function') {\n debug('plugin does not implement allow_unpublish');\n return next();\n }\n\n plugin.allow_unpublish(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden unpublish. Error: %o', err);\n return callback(err);\n }\n\n // The following is different from the allow_access and allow_publish implementations:\n // If the packages config is missing an entry for \"unpublish\", the built-in default method\n // (or a plugin) will return undefined, which will trigger the allow_publish fallback.\n // (see utils.ts, handleSpecialUnpublish, callback(null, undefined))\n if (isNil(ok) === true) {\n debug('bypass unpublish for %o, publish will handle the access', packageName);\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `bypass unpublish for @{name} by @{user}, publish will handle the access`\n );\n return this.allow_publish({ packageName, packageVersion }, user, callback);\n }\n\n if (ok) {\n debug('unpublish was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `unpublish was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('unpublish was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate unpublish denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n /**\n * Allow user to publish a package.\n */\n public allow_publish(\n { packageName, packageVersion }: pluginUtils.AuthPluginPackage,\n user: RemoteUser,\n callback: Callback\n ): void {\n const plugins = this.plugins.slice(0);\n const pkg = Object.assign(\n { name: packageName, version: packageVersion },\n authUtils.getMatchedPackagesSpec(packageName, this.config.packages)\n );\n\n debug('check publish permissions for user %o to package %o', user.name, packageName);\n\n // Use const instead of function declaration so we can use this.logger\n const next = (): void => {\n const plugin = plugins.shift();\n\n if (typeof plugin?.allow_publish !== 'function') {\n debug('plugin does not implement allow_publish');\n return next();\n }\n\n plugin.allow_publish(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {\n if (err) {\n debug('forbidden publish. Error: %o', err);\n return callback(err);\n }\n\n if (ok) {\n debug('publish was granted');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `publish was granted for @{name} by @{user}`\n );\n return callback(null, ok);\n }\n\n // cb(null, false) causes next plugin to roll\n debug('publish was denied. Rolling to next plugin');\n this.logger.trace(\n { user: user.name, name: pkg.name },\n `intermediate publish denial for @{name} by @{user}, rolling to next plugin`\n );\n return next();\n });\n };\n\n return next();\n }\n\n public apiJWTmiddleware(): any {\n debug('jwt middleware');\n const plugins = this.plugins.slice(0);\n const helpers = { createAnonymousRemoteUser, createRemoteUser };\n for (const plugin of plugins) {\n if (plugin.apiJWTmiddleware) {\n return plugin.apiJWTmiddleware(helpers);\n }\n }\n\n return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction) => {\n req.pause();\n const next = function (err?: VerdaccioError): NextFunction {\n req.resume();\n if (err) {\n return _next(err) as unknown as NextFunction;\n }\n\n return _next() as unknown as NextFunction;\n };\n\n // FUTURE: disabled, not removed yet but seems unreacable code\n // if (this._isRemoteUserValid(req.remote_user)) {\n // debug('jwt has a valid authentication header');\n // return next();\n // }\n\n // in case auth header does not exist we return anonymous function\n const remoteUser = createAnonymousRemoteUser();\n req.remote_user = remoteUser;\n res.locals.remote_user = remoteUser;\n\n const { authorization } = req.headers;\n if (isNil(authorization)) {\n debug('jwt, authentication header is missing');\n return next();\n }\n\n if (!isAuthHeaderValid(authorization)) {\n debug('api middleware authentication heather is invalid');\n return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));\n }\n const { secret, security } = this.config;\n\n if (isAESLegacy(security)) {\n debug('api middleware using legacy auth token');\n this.handleAESMiddleware(req, security, secret, authorization, next);\n } else {\n debug('api middleware using JWT auth token');\n this.handleJWTAPIMiddleware(req, security, secret, authorization, next);\n }\n };\n }\n\n private handleJWTAPIMiddleware(\n req: $RequestExtend,\n security: Security,\n secret: string,\n authorization: string,\n next: any\n ): void {\n debug('handle JWT api middleware');\n const credentials: any = getMiddlewareCredentials(security, secret, authorization);\n if (credentials) {\n // if the signature is valid we rely on it\n req.remote_user = credentials;\n debug('generating a remote user');\n next();\n } else {\n // with JWT throw 401\n debug('jwt invalid token');\n next(errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));\n }\n }\n\n private handleAESMiddleware(\n req: $RequestExtend,\n security: Security,\n secret: string,\n authorization: string,\n next: Function\n ): void {\n debug('handle legacy api middleware');\n debug('api middleware has a secret? %o', typeof secret === 'string');\n debug('api middleware authorization %o', typeof authorization === 'string');\n const credentials: any = getMiddlewareCredentials(security, secret, authorization);\n debug('api middleware credentials %o', credentials?.name);\n if (credentials) {\n const cacheKey = this.getLegacyAuthCacheKey(authorization);\n const cachedUser = this.getLegacyAuthCacheEntry(cacheKey);\n if (cachedUser) {\n req.remote_user = cachedUser;\n debug('generating cached remote user');\n return next();\n }\n\n const { user, password } = credentials;\n const applyAuthResult = (err: VerdaccioError | null, user?: RemoteUser): void => {\n if (!err && user) {\n req.remote_user = credentials.tokenKey\n ? { ...user, token: { key: credentials.tokenKey } }\n : user;\n debug('generating a remote user');\n next();\n } else {\n req.remote_user = createAnonymousRemoteUser();\n debug('generating anonymous user');\n next(err || errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));\n }\n };\n // concurrent requests for the same token wait for the in-flight one\n if (this.enqueueLegacyAuthCacheWaiter(cacheKey, applyAuthResult)) {\n return;\n }\n\n debug('authenticating %o', user);\n const onAuthComplete = (err: VerdaccioError | null, user?: RemoteUser): void => {\n // only the leader writes the cache; waiters just reuse its result\n if (!err && user) {\n this.setLegacyAuthCacheEntry(\n cacheKey,\n credentials.tokenKey ? { ...user, token: { key: credentials.tokenKey } } : user\n );\n }\n applyAuthResult(err, user);\n this.resolveLegacyAuthCacheWaiters(cacheKey, err, user);\n };\n try {\n this.authenticate(user, password, onAuthComplete);\n } catch (err: any) {\n onAuthComplete(errorUtils.getInternalError(err?.message));\n }\n } else {\n const remoteUser = this.getJWTRemoteUserFromBearer(authorization);\n if (remoteUser) {\n req.remote_user = remoteUser;\n debug('generating a remote user from jwt bearer');\n return next();\n }\n\n debug('legacy invalid header');\n return next(errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));\n }\n }\n\n private getJWTRemoteUserFromBearer(authorization: string): RemoteUser | void {\n const { scheme, token } = parseAuthTokenHeader(authorization);\n if (scheme.toUpperCase() !== TOKEN_BEARER.toUpperCase() || !token) {\n return;\n }\n\n let credentials: RemoteUser | undefined;\n try {\n credentials = verifyJWTPayload(token, this.config.secret, this.config.security);\n } catch {\n return;\n }\n\n if (this._isRemoteUserValid(credentials)) {\n const { name, groups } = credentials as RemoteUser;\n return createRemoteUser(name as string, groups);\n }\n }\n\n private enqueueLegacyAuthCacheWaiter(\n cacheKey: string | void,\n waiter: LegacyAuthCacheWaiter\n ): boolean {\n if (!cacheKey) {\n return false;\n }\n\n const waiters = this.legacyAuthCacheWaiters.get(cacheKey);\n if (!waiters) {\n this.legacyAuthCacheWaiters.set(cacheKey, []);\n return false;\n }\n\n waiters.push(waiter);\n return true;\n }\n\n private resolveLegacyAuthCacheWaiters(\n cacheKey: string | void,\n err: VerdaccioError | null,\n user?: RemoteUser\n ): void {\n if (!cacheKey) {\n return;\n }\n\n const waiters = this.legacyAuthCacheWaiters.get(cacheKey);\n this.legacyAuthCacheWaiters.delete(cacheKey);\n if (!waiters) {\n return;\n }\n\n for (const waiter of waiters) {\n waiter(err, user);\n }\n }\n\n private isLegacyAuthCacheEnabled(): boolean {\n // opt-in: disabled unless explicitly turned on via config\n return this.config.server?.legacyAuthCache?.enabled === true;\n }\n\n private getLegacyAuthCacheTtlMs(): number {\n const ttlMs = this.config.server?.legacyAuthCache?.ttlMs;\n return typeof ttlMs === 'number' && ttlMs > 0 ? ttlMs : 30 * 1000;\n }\n\n private getLegacyAuthCacheMaxEntries(): number {\n const maxEntries = this.config.server?.legacyAuthCache?.maxEntries;\n return typeof maxEntries === 'number' && maxEntries > 0 ? maxEntries : 1000;\n }\n\n private getLegacyAuthCacheKey(authorization: string): string | void {\n if (!this.isLegacyAuthCacheEnabled()) {\n return;\n }\n\n const { scheme } = parseAuthTokenHeader(authorization);\n if (scheme.toUpperCase() !== TOKEN_BEARER.toUpperCase()) {\n return;\n }\n\n return createHash('sha256').update(authorization).digest('hex');\n }\n\n private getLegacyAuthCacheEntry(cacheKey: string | void): RemoteUser | void {\n if (!cacheKey) {\n return;\n }\n\n const entry = this.legacyAuthCache.get(cacheKey);\n if (!entry) {\n return;\n }\n\n if (entry.expiresAt <= Date.now()) {\n this.legacyAuthCache.delete(cacheKey);\n return;\n }\n\n this.legacyAuthCache.delete(cacheKey);\n this.legacyAuthCache.set(cacheKey, entry);\n return cloneRemoteUser(entry.user);\n }\n\n private setLegacyAuthCacheEntry(cacheKey: string | void, user: RemoteUser): void {\n if (!cacheKey) {\n return;\n }\n\n this.legacyAuthCache.set(cacheKey, {\n expiresAt: Date.now() + this.getLegacyAuthCacheTtlMs(),\n user: cloneRemoteUser(user),\n });\n\n const maxEntries = this.getLegacyAuthCacheMaxEntries();\n while (this.legacyAuthCache.size > maxEntries) {\n const oldestKey = this.legacyAuthCache.keys().next().value;\n if (!oldestKey) {\n break;\n }\n this.legacyAuthCache.delete(oldestKey);\n }\n }\n\n private _isRemoteUserValid(remote_user?: RemoteUser): boolean {\n return isUndefined(remote_user) === false && isUndefined(remote_user?.name) === false;\n }\n\n /**\n * JWT middleware for WebUI\n */\n public webUIJWTmiddleware() {\n return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction): void => {\n if (this._isRemoteUserValid(req.remote_user)) {\n return _next();\n }\n\n req.pause();\n const next = (err: VerdaccioError | void): void => {\n req.resume();\n if (err) {\n req.remote_user.error = err.message;\n res.status(err.statusCode).send(err.message);\n }\n\n return _next();\n };\n\n const { authorization } = req.headers;\n if (isNil(authorization)) {\n req.remote_user = createAnonymousRemoteUser();\n return next();\n }\n\n if (!isAuthHeaderValid(authorization)) {\n return next(errorUtils.getBadRequest(API_ERROR.BAD_AUTH_HEADER));\n }\n\n const token = (authorization || '').replace(`${TOKEN_BEARER} `, '');\n if (!token) {\n return next();\n }\n\n let credentials: RemoteUser | undefined;\n try {\n credentials = verifyJWTPayload(token, this.config.secret, this.config.security);\n } catch {\n // FIXME: intended behaviour, do we want it?\n }\n\n if (this._isRemoteUserValid(credentials)) {\n const { name, groups } = credentials as RemoteUser;\n req.remote_user = createRemoteUser(name as string, groups);\n } else {\n req.remote_user = createAnonymousRemoteUser();\n }\n\n next();\n };\n }\n\n public async jwtEncrypt(user: RemoteUser, signOptions: JWTSignOptions): Promise<string> {\n const { real_groups, name, groups, token: tokenMetadata } = user;\n debug('jwt encrypt %o', name);\n const realGroupsValidated = isNil(real_groups) ? [] : real_groups;\n const groupedGroups = isNil(groups)\n ? real_groups\n : Array.from(new Set([...groups.concat(realGroupsValidated)]));\n const payload: RemoteUser = {\n real_groups: realGroupsValidated,\n name,\n groups: groupedGroups,\n };\n if (tokenMetadata?.key) {\n payload.token = { key: tokenMetadata.key };\n }\n const signedToken: string = await signPayload(\n payload,\n this.secret,\n signOptions as Parameters<typeof signPayload>[2]\n );\n\n return signedToken;\n }\n\n /**\n * Encrypt a string.\n */\n public aesEncrypt(value: string): string | void {\n debug('signing with aes encryption');\n const token = aesEncrypt(value, this.secret);\n return token;\n }\n}\n\nexport { Auth };\n"],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -7,7 +7,6 @@ const require_auth = require("./auth.js");
|
|
|
7
7
|
exports.Auth = require_auth.Auth;
|
|
8
8
|
exports.allow_action = require_utils.allow_action;
|
|
9
9
|
exports.buildUser = require_utils.buildUser;
|
|
10
|
-
exports.convertPayloadToBase64 = require_utils.convertPayloadToBase64;
|
|
11
10
|
exports.expireReasons = require_utils.expireReasons;
|
|
12
11
|
exports.getApiToken = require_utils.getApiToken;
|
|
13
12
|
exports.getDefaultPluginMethods = require_utils.getDefaultPluginMethods;
|
package/build/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { allow_action, buildUser,
|
|
1
|
+
import { allow_action, buildUser, expireReasons, getApiToken, getDefaultPluginMethods, getMiddlewareCredentials, handleSpecialUnpublish, isAESLegacy, isAuthHeaderValid, parseAESCredentials, parseAuthTokenHeader, verifyJWTPayload } from "./utils.mjs";
|
|
2
2
|
import { Auth } from "./auth.mjs";
|
|
3
|
-
export { Auth, allow_action, buildUser,
|
|
3
|
+
export { Auth, allow_action, buildUser, expireReasons, getApiToken, getDefaultPluginMethods, getMiddlewareCredentials, handleSpecialUnpublish, isAESLegacy, isAuthHeaderValid, parseAESCredentials, parseAuthTokenHeader, verifyJWTPayload };
|
package/build/utils.d.ts
CHANGED
|
@@ -27,4 +27,3 @@ export declare function allow_action(action: ActionsAllowed, logger: Logger): Al
|
|
|
27
27
|
*/
|
|
28
28
|
export declare function handleSpecialUnpublish(logger: Logger): any;
|
|
29
29
|
export declare function buildUser(name: string, password: string, tokenKey?: string): string;
|
|
30
|
-
export declare function convertPayloadToBase64(payload: string): Buffer;
|
package/build/utils.js
CHANGED
|
@@ -21,10 +21,7 @@ function parseAuthTokenHeader(authorizationHeader) {
|
|
|
21
21
|
function parseAESCredentials(authorizationHeader, secret) {
|
|
22
22
|
debug$1("parseAESCredentials init");
|
|
23
23
|
const { scheme, token } = parseAuthTokenHeader(authorizationHeader);
|
|
24
|
-
if (scheme.toUpperCase() === _verdaccio_core.
|
|
25
|
-
debug$1("legacy header basic");
|
|
26
|
-
return convertPayloadToBase64(token).toString();
|
|
27
|
-
} else if (scheme.toUpperCase() === _verdaccio_core.TOKEN_BEARER.toUpperCase()) {
|
|
24
|
+
if (scheme.toUpperCase() === _verdaccio_core.TOKEN_BEARER.toUpperCase()) {
|
|
28
25
|
debug$1("legacy header bearer");
|
|
29
26
|
return (0, _verdaccio_signature.aesDecrypt)(token.toString(), secret);
|
|
30
27
|
}
|
|
@@ -161,13 +158,9 @@ function buildUser(name, password, tokenKey) {
|
|
|
161
158
|
});
|
|
162
159
|
return String(`${name}:${password}`);
|
|
163
160
|
}
|
|
164
|
-
function convertPayloadToBase64(payload) {
|
|
165
|
-
return Buffer.from(payload, "base64");
|
|
166
|
-
}
|
|
167
161
|
//#endregion
|
|
168
162
|
exports.allow_action = allow_action;
|
|
169
163
|
exports.buildUser = buildUser;
|
|
170
|
-
exports.convertPayloadToBase64 = convertPayloadToBase64;
|
|
171
164
|
exports.expireReasons = expireReasons;
|
|
172
165
|
exports.getApiToken = getApiToken;
|
|
173
166
|
exports.getDefaultPluginMethods = getDefaultPluginMethods;
|
package/build/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { isNil } from 'lodash-es';\n\nimport { createAnonymousRemoteUser } from '@verdaccio/config';\nimport type { pluginUtils } from '@verdaccio/core';\nimport { API_ERROR, HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER, errorUtils } from '@verdaccio/core';\nimport { aesDecrypt, parseBasicPayload, verifyPayload } from '@verdaccio/signature';\nimport type { AuthPackageAllow, Config, Logger, RemoteUser, Security } from '@verdaccio/types';\n\nimport type {\n ActionsAllowed,\n AllowAction,\n AllowActionCallback,\n AuthMiddlewarePayload,\n AuthTokenHeader,\n TokenEncryption,\n} from './types';\n\nconst debug = buildDebug('verdaccio:auth:utils');\n\n/**\n * Split authentication header eg: Bearer [secret_token]\n * @param authorizationHeader auth token\n */\nexport function parseAuthTokenHeader(authorizationHeader: string): AuthTokenHeader {\n const parts = authorizationHeader.split(' ');\n const [scheme, token] = parts;\n\n return { scheme, token };\n}\n\nexport function parseAESCredentials(authorizationHeader: string, secret: string) {\n debug('parseAESCredentials init');\n const { scheme, token } = parseAuthTokenHeader(authorizationHeader);\n\n // basic is deprecated and should not be enforced\n // basic is currently being used for functional test\n if (scheme.toUpperCase() === TOKEN_BASIC.toUpperCase()) {\n debug('legacy header basic');\n const credentials = convertPayloadToBase64(token).toString();\n\n return credentials;\n } else if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {\n debug('legacy header bearer');\n return aesDecrypt(token.toString(), secret);\n }\n}\n\nexport function getMiddlewareCredentials(\n security: Security,\n secretKey: string,\n authorizationHeader: string\n): AuthMiddlewarePayload {\n debug('getMiddlewareCredentials init');\n // comment out for debugging purposes\n if (isAESLegacy(security)) {\n debug('is legacy');\n const credentials = parseAESCredentials(authorizationHeader, secretKey);\n if (!credentials) {\n debug('parse legacy credentials failed');\n return;\n }\n\n const parsedCredentials = parseBasicPayload(credentials);\n if (!parsedCredentials) {\n debug('parse legacy basic payload credentials failed');\n return;\n }\n\n return parsedCredentials;\n }\n const { scheme, token } = parseAuthTokenHeader(authorizationHeader);\n\n debug('is jwt');\n if (typeof token === 'string' && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {\n return verifyJWTPayload(token, secretKey, security);\n }\n}\n\nexport function isAESLegacy(security: Security): boolean {\n const { legacy, jwt } = security.api;\n\n return isNil(legacy) === false && isNil(jwt) && legacy === true;\n}\n\nexport async function getApiToken(\n auth: TokenEncryption,\n config: Config,\n remoteUser: RemoteUser,\n aesPassword: string,\n options: { tokenKey?: string } = {}\n): Promise<string | void> {\n debug('get api token');\n const { security } = config;\n const tokenUser: RemoteUser = options.tokenKey\n ? { ...remoteUser, token: { key: options.tokenKey } }\n : remoteUser;\n\n if (isAESLegacy(security)) {\n debug('security legacy enabled');\n // fallback all goes to AES encryption\n return await new Promise((resolve): void => {\n resolve(auth.aesEncrypt(buildUser(remoteUser.name as string, aesPassword, options.tokenKey)));\n });\n }\n const { jwt } = security.api;\n\n if (jwt?.sign) {\n return await auth.jwtEncrypt(tokenUser, jwt.sign);\n }\n return await new Promise((resolve): void => {\n resolve(auth.aesEncrypt(buildUser(remoteUser.name as string, aesPassword, options.tokenKey)));\n });\n}\n\nexport const expireReasons: string[] = ['JsonWebTokenError', 'TokenExpiredError'];\n\nexport function verifyJWTPayload(token: string, secret: string, security: Security): RemoteUser {\n try {\n const payload: RemoteUser = verifyPayload(\n token,\n secret,\n security?.api?.jwt?.verify as Parameters<typeof verifyPayload>[2]\n );\n\n return payload;\n } catch (error: any) {\n // #168 this check should be removed as soon AES encrypt is removed.\n if (expireReasons.includes(error.name)) {\n // it might be possible the jwt configuration is enabled and\n // old tokens fails still remains in usage, thus\n // we return an anonymous user to force log in.\n return createAnonymousRemoteUser();\n }\n throw errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, error.message);\n }\n}\n\nexport function isAuthHeaderValid(authorization: string): boolean {\n return authorization.split(' ').length === 2;\n}\n\n/**\n * Return a default configuration for authentication if none is provided.\n * @param logger {Logger}\n * @returns object of default implementations.\n */\nexport function getDefaultPluginMethods(logger: Logger): pluginUtils.Auth<Config> {\n return {\n authenticate(_user: string, _password: string, cb: pluginUtils.AuthCallback): void {\n debug('triggered default authenticate method');\n cb(errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));\n },\n\n adduser(_user: string, _password: string, cb: pluginUtils.AuthUserCallback): void {\n debug('triggered default adduser method');\n // since adduser is not implemented but optional, continue without error\n // this assumes that the user is added by an external system\n cb(null, true);\n },\n\n // @ts-ignore\n allow_access: allow_action('access', logger),\n // @ts-ignore\n allow_publish: allow_action('publish', logger),\n allow_unpublish: handleSpecialUnpublish(logger),\n };\n}\n\nexport function allow_action(action: ActionsAllowed, logger: Logger): AllowAction {\n return function allowActionCallback(\n user: RemoteUser,\n pkg: AuthPackageAllow,\n callback: AllowActionCallback\n ): void {\n logger.trace({ remote: user.name }, `[auth/allow_action]: user: @{remote}`);\n const { name, groups } = user;\n debug('allow_action \"%s\": groups %s', action, groups);\n const groupAccess = pkg[action] as string[];\n debug('allow_action \"%s\": groupAccess %s', action, groupAccess);\n const hasPermission = groupAccess.some((group) => {\n return name === group || groups.includes(group);\n });\n debug('package \"%s\" has permission \"%s\"', name, hasPermission);\n logger.trace(\n { pkgName: pkg.name, hasPermission, remote: user.name, groupAccess },\n `[auth/allow_action]: hasPermission? @{hasPermission} for user: @{remote}, package: @{pkgName}`\n );\n\n if (hasPermission) {\n logger.trace({ remote: user.name }, `auth/allow_action: access granted to: @{remote}`);\n return callback(null, true);\n }\n\n if (name) {\n callback(\n errorUtils.getForbidden(`user ${name} is not allowed to ${action} package ${pkg.name}`)\n );\n } else {\n callback(\n errorUtils.getUnauthorized(`authorization required to ${action} package ${pkg.name}`)\n );\n }\n };\n}\n\n/**\n *\n */\nexport function handleSpecialUnpublish(logger: Logger): any {\n return function (user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {\n const action = 'unpublish';\n // verify whether the unpublish prop has been defined\n const isUnpublishMissing: boolean = !pkg[action];\n debug('is unpublish method missing ? %s', isUnpublishMissing);\n const hasGroups: boolean = isUnpublishMissing ? false : (pkg[action] as string[]).length > 0;\n logger.trace(\n { user: user.name, name: pkg.name, hasGroups },\n `fallback unpublish for @{name} has groups: @{hasGroups} for @{user}`\n );\n\n if (isUnpublishMissing || hasGroups === false) {\n return callback(null, undefined);\n }\n\n logger.trace(\n { user: user.name, name: pkg.name, action, hasGroups },\n `allow_action for @{action} for @{name} has groups: @{hasGroups} for @{user}`\n );\n return allow_action(action, logger)(user, pkg, callback);\n };\n}\n\nexport function buildUser(name: string, password: string, tokenKey?: string): string {\n if (tokenKey) {\n return JSON.stringify({ user: name, password, tokenKey });\n }\n\n return String(`${name}:${password}`);\n}\n\nexport function convertPayloadToBase64(payload: string): Buffer {\n return Buffer.from(payload, 'base64');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { isNil } from 'lodash-es';\n\nimport { createAnonymousRemoteUser } from '@verdaccio/config';\nimport type { pluginUtils } from '@verdaccio/core';\nimport { API_ERROR, HTTP_STATUS, TOKEN_BEARER, errorUtils } from '@verdaccio/core';\nimport { aesDecrypt, parseBasicPayload, verifyPayload } from '@verdaccio/signature';\nimport type { AuthPackageAllow, Config, Logger, RemoteUser, Security } from '@verdaccio/types';\n\nimport type {\n ActionsAllowed,\n AllowAction,\n AllowActionCallback,\n AuthMiddlewarePayload,\n AuthTokenHeader,\n TokenEncryption,\n} from './types';\n\nconst debug = buildDebug('verdaccio:auth:utils');\n\n/**\n * Split authentication header eg: Bearer [secret_token]\n * @param authorizationHeader auth token\n */\nexport function parseAuthTokenHeader(authorizationHeader: string): AuthTokenHeader {\n const parts = authorizationHeader.split(' ');\n const [scheme, token] = parts;\n\n return { scheme, token };\n}\n\nexport function parseAESCredentials(authorizationHeader: string, secret: string) {\n debug('parseAESCredentials init');\n const { scheme, token } = parseAuthTokenHeader(authorizationHeader);\n\n if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {\n debug('legacy header bearer');\n return aesDecrypt(token.toString(), secret);\n }\n}\n\nexport function getMiddlewareCredentials(\n security: Security,\n secretKey: string,\n authorizationHeader: string\n): AuthMiddlewarePayload {\n debug('getMiddlewareCredentials init');\n // comment out for debugging purposes\n if (isAESLegacy(security)) {\n debug('is legacy');\n const credentials = parseAESCredentials(authorizationHeader, secretKey);\n if (!credentials) {\n debug('parse legacy credentials failed');\n return;\n }\n\n const parsedCredentials = parseBasicPayload(credentials);\n if (!parsedCredentials) {\n debug('parse legacy basic payload credentials failed');\n return;\n }\n\n return parsedCredentials;\n }\n const { scheme, token } = parseAuthTokenHeader(authorizationHeader);\n\n debug('is jwt');\n if (typeof token === 'string' && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {\n return verifyJWTPayload(token, secretKey, security);\n }\n}\n\nexport function isAESLegacy(security: Security): boolean {\n const { legacy, jwt } = security.api;\n\n return isNil(legacy) === false && isNil(jwt) && legacy === true;\n}\n\nexport async function getApiToken(\n auth: TokenEncryption,\n config: Config,\n remoteUser: RemoteUser,\n aesPassword: string,\n options: { tokenKey?: string } = {}\n): Promise<string | void> {\n debug('get api token');\n const { security } = config;\n const tokenUser: RemoteUser = options.tokenKey\n ? { ...remoteUser, token: { key: options.tokenKey } }\n : remoteUser;\n\n if (isAESLegacy(security)) {\n debug('security legacy enabled');\n // fallback all goes to AES encryption\n return await new Promise((resolve): void => {\n resolve(auth.aesEncrypt(buildUser(remoteUser.name as string, aesPassword, options.tokenKey)));\n });\n }\n const { jwt } = security.api;\n\n if (jwt?.sign) {\n return await auth.jwtEncrypt(tokenUser, jwt.sign);\n }\n return await new Promise((resolve): void => {\n resolve(auth.aesEncrypt(buildUser(remoteUser.name as string, aesPassword, options.tokenKey)));\n });\n}\n\nexport const expireReasons: string[] = ['JsonWebTokenError', 'TokenExpiredError'];\n\nexport function verifyJWTPayload(token: string, secret: string, security: Security): RemoteUser {\n try {\n const payload: RemoteUser = verifyPayload(\n token,\n secret,\n security?.api?.jwt?.verify as Parameters<typeof verifyPayload>[2]\n );\n\n return payload;\n } catch (error: any) {\n // #168 this check should be removed as soon AES encrypt is removed.\n if (expireReasons.includes(error.name)) {\n // it might be possible the jwt configuration is enabled and\n // old tokens fails still remains in usage, thus\n // we return an anonymous user to force log in.\n return createAnonymousRemoteUser();\n }\n throw errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, error.message);\n }\n}\n\nexport function isAuthHeaderValid(authorization: string): boolean {\n return authorization.split(' ').length === 2;\n}\n\n/**\n * Return a default configuration for authentication if none is provided.\n * @param logger {Logger}\n * @returns object of default implementations.\n */\nexport function getDefaultPluginMethods(logger: Logger): pluginUtils.Auth<Config> {\n return {\n authenticate(_user: string, _password: string, cb: pluginUtils.AuthCallback): void {\n debug('triggered default authenticate method');\n cb(errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));\n },\n\n adduser(_user: string, _password: string, cb: pluginUtils.AuthUserCallback): void {\n debug('triggered default adduser method');\n // since adduser is not implemented but optional, continue without error\n // this assumes that the user is added by an external system\n cb(null, true);\n },\n\n // @ts-ignore\n allow_access: allow_action('access', logger),\n // @ts-ignore\n allow_publish: allow_action('publish', logger),\n allow_unpublish: handleSpecialUnpublish(logger),\n };\n}\n\nexport function allow_action(action: ActionsAllowed, logger: Logger): AllowAction {\n return function allowActionCallback(\n user: RemoteUser,\n pkg: AuthPackageAllow,\n callback: AllowActionCallback\n ): void {\n logger.trace({ remote: user.name }, `[auth/allow_action]: user: @{remote}`);\n const { name, groups } = user;\n debug('allow_action \"%s\": groups %s', action, groups);\n const groupAccess = pkg[action] as string[];\n debug('allow_action \"%s\": groupAccess %s', action, groupAccess);\n const hasPermission = groupAccess.some((group) => {\n return name === group || groups.includes(group);\n });\n debug('package \"%s\" has permission \"%s\"', name, hasPermission);\n logger.trace(\n { pkgName: pkg.name, hasPermission, remote: user.name, groupAccess },\n `[auth/allow_action]: hasPermission? @{hasPermission} for user: @{remote}, package: @{pkgName}`\n );\n\n if (hasPermission) {\n logger.trace({ remote: user.name }, `auth/allow_action: access granted to: @{remote}`);\n return callback(null, true);\n }\n\n if (name) {\n callback(\n errorUtils.getForbidden(`user ${name} is not allowed to ${action} package ${pkg.name}`)\n );\n } else {\n callback(\n errorUtils.getUnauthorized(`authorization required to ${action} package ${pkg.name}`)\n );\n }\n };\n}\n\n/**\n *\n */\nexport function handleSpecialUnpublish(logger: Logger): any {\n return function (user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {\n const action = 'unpublish';\n // verify whether the unpublish prop has been defined\n const isUnpublishMissing: boolean = !pkg[action];\n debug('is unpublish method missing ? %s', isUnpublishMissing);\n const hasGroups: boolean = isUnpublishMissing ? false : (pkg[action] as string[]).length > 0;\n logger.trace(\n { user: user.name, name: pkg.name, hasGroups },\n `fallback unpublish for @{name} has groups: @{hasGroups} for @{user}`\n );\n\n if (isUnpublishMissing || hasGroups === false) {\n return callback(null, undefined);\n }\n\n logger.trace(\n { user: user.name, name: pkg.name, action, hasGroups },\n `allow_action for @{action} for @{name} has groups: @{hasGroups} for @{user}`\n );\n return allow_action(action, logger)(user, pkg, callback);\n };\n}\n\nexport function buildUser(name: string, password: string, tokenKey?: string): string {\n if (tokenKey) {\n return JSON.stringify({ user: name, password, tokenKey });\n }\n\n return String(`${name}:${password}`);\n}\n"],"mappings":"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"}
|
package/build/utils.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import buildDebug from "debug";
|
|
2
2
|
import { isNil } from "lodash-es";
|
|
3
3
|
import { createAnonymousRemoteUser } from "@verdaccio/config";
|
|
4
|
-
import { API_ERROR, HTTP_STATUS,
|
|
4
|
+
import { API_ERROR, HTTP_STATUS, TOKEN_BEARER, errorUtils } from "@verdaccio/core";
|
|
5
5
|
import { aesDecrypt, parseBasicPayload, verifyPayload } from "@verdaccio/signature";
|
|
6
6
|
//#region src/utils.ts
|
|
7
7
|
var debug = buildDebug("verdaccio:auth:utils");
|
|
@@ -19,10 +19,7 @@ function parseAuthTokenHeader(authorizationHeader) {
|
|
|
19
19
|
function parseAESCredentials(authorizationHeader, secret) {
|
|
20
20
|
debug("parseAESCredentials init");
|
|
21
21
|
const { scheme, token } = parseAuthTokenHeader(authorizationHeader);
|
|
22
|
-
if (scheme.toUpperCase() ===
|
|
23
|
-
debug("legacy header basic");
|
|
24
|
-
return convertPayloadToBase64(token).toString();
|
|
25
|
-
} else if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
|
|
22
|
+
if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
|
|
26
23
|
debug("legacy header bearer");
|
|
27
24
|
return aesDecrypt(token.toString(), secret);
|
|
28
25
|
}
|
|
@@ -159,10 +156,7 @@ function buildUser(name, password, tokenKey) {
|
|
|
159
156
|
});
|
|
160
157
|
return String(`${name}:${password}`);
|
|
161
158
|
}
|
|
162
|
-
function convertPayloadToBase64(payload) {
|
|
163
|
-
return Buffer.from(payload, "base64");
|
|
164
|
-
}
|
|
165
159
|
//#endregion
|
|
166
|
-
export { allow_action, buildUser,
|
|
160
|
+
export { allow_action, buildUser, expireReasons, getApiToken, getDefaultPluginMethods, getMiddlewareCredentials, handleSpecialUnpublish, isAESLegacy, isAuthHeaderValid, parseAESCredentials, parseAuthTokenHeader, verifyJWTPayload };
|
|
167
161
|
|
|
168
162
|
//# sourceMappingURL=utils.mjs.map
|
package/build/utils.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { isNil } from 'lodash-es';\n\nimport { createAnonymousRemoteUser } from '@verdaccio/config';\nimport type { pluginUtils } from '@verdaccio/core';\nimport { API_ERROR, HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER, errorUtils } from '@verdaccio/core';\nimport { aesDecrypt, parseBasicPayload, verifyPayload } from '@verdaccio/signature';\nimport type { AuthPackageAllow, Config, Logger, RemoteUser, Security } from '@verdaccio/types';\n\nimport type {\n ActionsAllowed,\n AllowAction,\n AllowActionCallback,\n AuthMiddlewarePayload,\n AuthTokenHeader,\n TokenEncryption,\n} from './types';\n\nconst debug = buildDebug('verdaccio:auth:utils');\n\n/**\n * Split authentication header eg: Bearer [secret_token]\n * @param authorizationHeader auth token\n */\nexport function parseAuthTokenHeader(authorizationHeader: string): AuthTokenHeader {\n const parts = authorizationHeader.split(' ');\n const [scheme, token] = parts;\n\n return { scheme, token };\n}\n\nexport function parseAESCredentials(authorizationHeader: string, secret: string) {\n debug('parseAESCredentials init');\n const { scheme, token } = parseAuthTokenHeader(authorizationHeader);\n\n // basic is deprecated and should not be enforced\n // basic is currently being used for functional test\n if (scheme.toUpperCase() === TOKEN_BASIC.toUpperCase()) {\n debug('legacy header basic');\n const credentials = convertPayloadToBase64(token).toString();\n\n return credentials;\n } else if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {\n debug('legacy header bearer');\n return aesDecrypt(token.toString(), secret);\n }\n}\n\nexport function getMiddlewareCredentials(\n security: Security,\n secretKey: string,\n authorizationHeader: string\n): AuthMiddlewarePayload {\n debug('getMiddlewareCredentials init');\n // comment out for debugging purposes\n if (isAESLegacy(security)) {\n debug('is legacy');\n const credentials = parseAESCredentials(authorizationHeader, secretKey);\n if (!credentials) {\n debug('parse legacy credentials failed');\n return;\n }\n\n const parsedCredentials = parseBasicPayload(credentials);\n if (!parsedCredentials) {\n debug('parse legacy basic payload credentials failed');\n return;\n }\n\n return parsedCredentials;\n }\n const { scheme, token } = parseAuthTokenHeader(authorizationHeader);\n\n debug('is jwt');\n if (typeof token === 'string' && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {\n return verifyJWTPayload(token, secretKey, security);\n }\n}\n\nexport function isAESLegacy(security: Security): boolean {\n const { legacy, jwt } = security.api;\n\n return isNil(legacy) === false && isNil(jwt) && legacy === true;\n}\n\nexport async function getApiToken(\n auth: TokenEncryption,\n config: Config,\n remoteUser: RemoteUser,\n aesPassword: string,\n options: { tokenKey?: string } = {}\n): Promise<string | void> {\n debug('get api token');\n const { security } = config;\n const tokenUser: RemoteUser = options.tokenKey\n ? { ...remoteUser, token: { key: options.tokenKey } }\n : remoteUser;\n\n if (isAESLegacy(security)) {\n debug('security legacy enabled');\n // fallback all goes to AES encryption\n return await new Promise((resolve): void => {\n resolve(auth.aesEncrypt(buildUser(remoteUser.name as string, aesPassword, options.tokenKey)));\n });\n }\n const { jwt } = security.api;\n\n if (jwt?.sign) {\n return await auth.jwtEncrypt(tokenUser, jwt.sign);\n }\n return await new Promise((resolve): void => {\n resolve(auth.aesEncrypt(buildUser(remoteUser.name as string, aesPassword, options.tokenKey)));\n });\n}\n\nexport const expireReasons: string[] = ['JsonWebTokenError', 'TokenExpiredError'];\n\nexport function verifyJWTPayload(token: string, secret: string, security: Security): RemoteUser {\n try {\n const payload: RemoteUser = verifyPayload(\n token,\n secret,\n security?.api?.jwt?.verify as Parameters<typeof verifyPayload>[2]\n );\n\n return payload;\n } catch (error: any) {\n // #168 this check should be removed as soon AES encrypt is removed.\n if (expireReasons.includes(error.name)) {\n // it might be possible the jwt configuration is enabled and\n // old tokens fails still remains in usage, thus\n // we return an anonymous user to force log in.\n return createAnonymousRemoteUser();\n }\n throw errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, error.message);\n }\n}\n\nexport function isAuthHeaderValid(authorization: string): boolean {\n return authorization.split(' ').length === 2;\n}\n\n/**\n * Return a default configuration for authentication if none is provided.\n * @param logger {Logger}\n * @returns object of default implementations.\n */\nexport function getDefaultPluginMethods(logger: Logger): pluginUtils.Auth<Config> {\n return {\n authenticate(_user: string, _password: string, cb: pluginUtils.AuthCallback): void {\n debug('triggered default authenticate method');\n cb(errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));\n },\n\n adduser(_user: string, _password: string, cb: pluginUtils.AuthUserCallback): void {\n debug('triggered default adduser method');\n // since adduser is not implemented but optional, continue without error\n // this assumes that the user is added by an external system\n cb(null, true);\n },\n\n // @ts-ignore\n allow_access: allow_action('access', logger),\n // @ts-ignore\n allow_publish: allow_action('publish', logger),\n allow_unpublish: handleSpecialUnpublish(logger),\n };\n}\n\nexport function allow_action(action: ActionsAllowed, logger: Logger): AllowAction {\n return function allowActionCallback(\n user: RemoteUser,\n pkg: AuthPackageAllow,\n callback: AllowActionCallback\n ): void {\n logger.trace({ remote: user.name }, `[auth/allow_action]: user: @{remote}`);\n const { name, groups } = user;\n debug('allow_action \"%s\": groups %s', action, groups);\n const groupAccess = pkg[action] as string[];\n debug('allow_action \"%s\": groupAccess %s', action, groupAccess);\n const hasPermission = groupAccess.some((group) => {\n return name === group || groups.includes(group);\n });\n debug('package \"%s\" has permission \"%s\"', name, hasPermission);\n logger.trace(\n { pkgName: pkg.name, hasPermission, remote: user.name, groupAccess },\n `[auth/allow_action]: hasPermission? @{hasPermission} for user: @{remote}, package: @{pkgName}`\n );\n\n if (hasPermission) {\n logger.trace({ remote: user.name }, `auth/allow_action: access granted to: @{remote}`);\n return callback(null, true);\n }\n\n if (name) {\n callback(\n errorUtils.getForbidden(`user ${name} is not allowed to ${action} package ${pkg.name}`)\n );\n } else {\n callback(\n errorUtils.getUnauthorized(`authorization required to ${action} package ${pkg.name}`)\n );\n }\n };\n}\n\n/**\n *\n */\nexport function handleSpecialUnpublish(logger: Logger): any {\n return function (user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {\n const action = 'unpublish';\n // verify whether the unpublish prop has been defined\n const isUnpublishMissing: boolean = !pkg[action];\n debug('is unpublish method missing ? %s', isUnpublishMissing);\n const hasGroups: boolean = isUnpublishMissing ? false : (pkg[action] as string[]).length > 0;\n logger.trace(\n { user: user.name, name: pkg.name, hasGroups },\n `fallback unpublish for @{name} has groups: @{hasGroups} for @{user}`\n );\n\n if (isUnpublishMissing || hasGroups === false) {\n return callback(null, undefined);\n }\n\n logger.trace(\n { user: user.name, name: pkg.name, action, hasGroups },\n `allow_action for @{action} for @{name} has groups: @{hasGroups} for @{user}`\n );\n return allow_action(action, logger)(user, pkg, callback);\n };\n}\n\nexport function buildUser(name: string, password: string, tokenKey?: string): string {\n if (tokenKey) {\n return JSON.stringify({ user: name, password, tokenKey });\n }\n\n return String(`${name}:${password}`);\n}\n\nexport function convertPayloadToBase64(payload: string): Buffer {\n return Buffer.from(payload, 'base64');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { isNil } from 'lodash-es';\n\nimport { createAnonymousRemoteUser } from '@verdaccio/config';\nimport type { pluginUtils } from '@verdaccio/core';\nimport { API_ERROR, HTTP_STATUS, TOKEN_BEARER, errorUtils } from '@verdaccio/core';\nimport { aesDecrypt, parseBasicPayload, verifyPayload } from '@verdaccio/signature';\nimport type { AuthPackageAllow, Config, Logger, RemoteUser, Security } from '@verdaccio/types';\n\nimport type {\n ActionsAllowed,\n AllowAction,\n AllowActionCallback,\n AuthMiddlewarePayload,\n AuthTokenHeader,\n TokenEncryption,\n} from './types';\n\nconst debug = buildDebug('verdaccio:auth:utils');\n\n/**\n * Split authentication header eg: Bearer [secret_token]\n * @param authorizationHeader auth token\n */\nexport function parseAuthTokenHeader(authorizationHeader: string): AuthTokenHeader {\n const parts = authorizationHeader.split(' ');\n const [scheme, token] = parts;\n\n return { scheme, token };\n}\n\nexport function parseAESCredentials(authorizationHeader: string, secret: string) {\n debug('parseAESCredentials init');\n const { scheme, token } = parseAuthTokenHeader(authorizationHeader);\n\n if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {\n debug('legacy header bearer');\n return aesDecrypt(token.toString(), secret);\n }\n}\n\nexport function getMiddlewareCredentials(\n security: Security,\n secretKey: string,\n authorizationHeader: string\n): AuthMiddlewarePayload {\n debug('getMiddlewareCredentials init');\n // comment out for debugging purposes\n if (isAESLegacy(security)) {\n debug('is legacy');\n const credentials = parseAESCredentials(authorizationHeader, secretKey);\n if (!credentials) {\n debug('parse legacy credentials failed');\n return;\n }\n\n const parsedCredentials = parseBasicPayload(credentials);\n if (!parsedCredentials) {\n debug('parse legacy basic payload credentials failed');\n return;\n }\n\n return parsedCredentials;\n }\n const { scheme, token } = parseAuthTokenHeader(authorizationHeader);\n\n debug('is jwt');\n if (typeof token === 'string' && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {\n return verifyJWTPayload(token, secretKey, security);\n }\n}\n\nexport function isAESLegacy(security: Security): boolean {\n const { legacy, jwt } = security.api;\n\n return isNil(legacy) === false && isNil(jwt) && legacy === true;\n}\n\nexport async function getApiToken(\n auth: TokenEncryption,\n config: Config,\n remoteUser: RemoteUser,\n aesPassword: string,\n options: { tokenKey?: string } = {}\n): Promise<string | void> {\n debug('get api token');\n const { security } = config;\n const tokenUser: RemoteUser = options.tokenKey\n ? { ...remoteUser, token: { key: options.tokenKey } }\n : remoteUser;\n\n if (isAESLegacy(security)) {\n debug('security legacy enabled');\n // fallback all goes to AES encryption\n return await new Promise((resolve): void => {\n resolve(auth.aesEncrypt(buildUser(remoteUser.name as string, aesPassword, options.tokenKey)));\n });\n }\n const { jwt } = security.api;\n\n if (jwt?.sign) {\n return await auth.jwtEncrypt(tokenUser, jwt.sign);\n }\n return await new Promise((resolve): void => {\n resolve(auth.aesEncrypt(buildUser(remoteUser.name as string, aesPassword, options.tokenKey)));\n });\n}\n\nexport const expireReasons: string[] = ['JsonWebTokenError', 'TokenExpiredError'];\n\nexport function verifyJWTPayload(token: string, secret: string, security: Security): RemoteUser {\n try {\n const payload: RemoteUser = verifyPayload(\n token,\n secret,\n security?.api?.jwt?.verify as Parameters<typeof verifyPayload>[2]\n );\n\n return payload;\n } catch (error: any) {\n // #168 this check should be removed as soon AES encrypt is removed.\n if (expireReasons.includes(error.name)) {\n // it might be possible the jwt configuration is enabled and\n // old tokens fails still remains in usage, thus\n // we return an anonymous user to force log in.\n return createAnonymousRemoteUser();\n }\n throw errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, error.message);\n }\n}\n\nexport function isAuthHeaderValid(authorization: string): boolean {\n return authorization.split(' ').length === 2;\n}\n\n/**\n * Return a default configuration for authentication if none is provided.\n * @param logger {Logger}\n * @returns object of default implementations.\n */\nexport function getDefaultPluginMethods(logger: Logger): pluginUtils.Auth<Config> {\n return {\n authenticate(_user: string, _password: string, cb: pluginUtils.AuthCallback): void {\n debug('triggered default authenticate method');\n cb(errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));\n },\n\n adduser(_user: string, _password: string, cb: pluginUtils.AuthUserCallback): void {\n debug('triggered default adduser method');\n // since adduser is not implemented but optional, continue without error\n // this assumes that the user is added by an external system\n cb(null, true);\n },\n\n // @ts-ignore\n allow_access: allow_action('access', logger),\n // @ts-ignore\n allow_publish: allow_action('publish', logger),\n allow_unpublish: handleSpecialUnpublish(logger),\n };\n}\n\nexport function allow_action(action: ActionsAllowed, logger: Logger): AllowAction {\n return function allowActionCallback(\n user: RemoteUser,\n pkg: AuthPackageAllow,\n callback: AllowActionCallback\n ): void {\n logger.trace({ remote: user.name }, `[auth/allow_action]: user: @{remote}`);\n const { name, groups } = user;\n debug('allow_action \"%s\": groups %s', action, groups);\n const groupAccess = pkg[action] as string[];\n debug('allow_action \"%s\": groupAccess %s', action, groupAccess);\n const hasPermission = groupAccess.some((group) => {\n return name === group || groups.includes(group);\n });\n debug('package \"%s\" has permission \"%s\"', name, hasPermission);\n logger.trace(\n { pkgName: pkg.name, hasPermission, remote: user.name, groupAccess },\n `[auth/allow_action]: hasPermission? @{hasPermission} for user: @{remote}, package: @{pkgName}`\n );\n\n if (hasPermission) {\n logger.trace({ remote: user.name }, `auth/allow_action: access granted to: @{remote}`);\n return callback(null, true);\n }\n\n if (name) {\n callback(\n errorUtils.getForbidden(`user ${name} is not allowed to ${action} package ${pkg.name}`)\n );\n } else {\n callback(\n errorUtils.getUnauthorized(`authorization required to ${action} package ${pkg.name}`)\n );\n }\n };\n}\n\n/**\n *\n */\nexport function handleSpecialUnpublish(logger: Logger): any {\n return function (user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {\n const action = 'unpublish';\n // verify whether the unpublish prop has been defined\n const isUnpublishMissing: boolean = !pkg[action];\n debug('is unpublish method missing ? %s', isUnpublishMissing);\n const hasGroups: boolean = isUnpublishMissing ? false : (pkg[action] as string[]).length > 0;\n logger.trace(\n { user: user.name, name: pkg.name, hasGroups },\n `fallback unpublish for @{name} has groups: @{hasGroups} for @{user}`\n );\n\n if (isUnpublishMissing || hasGroups === false) {\n return callback(null, undefined);\n }\n\n logger.trace(\n { user: user.name, name: pkg.name, action, hasGroups },\n `allow_action for @{action} for @{name} has groups: @{hasGroups} for @{user}`\n );\n return allow_action(action, logger)(user, pkg, callback);\n };\n}\n\nexport function buildUser(name: string, password: string, tokenKey?: string): string {\n if (tokenKey) {\n return JSON.stringify({ user: name, password, tokenKey });\n }\n\n return String(`${name}:${password}`);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verdaccio/auth",
|
|
3
|
-
"version": "9.0.0-next-9.
|
|
3
|
+
"version": "9.0.0-next-9.27",
|
|
4
4
|
"description": "Verdaccio Authentication",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -33,18 +33,18 @@
|
|
|
33
33
|
},
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@verdaccio/config": "9.0.0-next-9.
|
|
37
|
-
"@verdaccio/core": "9.0.0-next-9.
|
|
38
|
-
"@verdaccio/loaders": "9.0.0-next-9.
|
|
39
|
-
"@verdaccio/signature": "9.0.0-next-9.
|
|
36
|
+
"@verdaccio/config": "9.0.0-next-9.27",
|
|
37
|
+
"@verdaccio/core": "9.0.0-next-9.27",
|
|
38
|
+
"@verdaccio/loaders": "9.0.0-next-9.27",
|
|
39
|
+
"@verdaccio/signature": "9.0.0-next-9.27",
|
|
40
40
|
"debug": "4.4.3",
|
|
41
41
|
"lodash-es": "4.18.1",
|
|
42
|
-
"verdaccio-htpasswd": "14.0.0-next-9.
|
|
42
|
+
"verdaccio-htpasswd": "14.0.0-next-9.27"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/lodash-es": "4.17.12",
|
|
46
|
-
"@verdaccio/logger": "9.0.0-next-9.
|
|
47
|
-
"@verdaccio/middleware": "9.0.0-next-9.
|
|
46
|
+
"@verdaccio/logger": "9.0.0-next-9.27",
|
|
47
|
+
"@verdaccio/middleware": "9.0.0-next-9.27",
|
|
48
48
|
"@verdaccio/types": "14.0.0-next-9.12",
|
|
49
49
|
"express": "5.2.1",
|
|
50
50
|
"rimraf": "6.1.3",
|