0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-01-06 22:40:26 -05:00

refactor: auth-utils (#1951)

* chore: refactor auth utils

* chore: relocate crypto utils
This commit is contained in:
Juan Picado 2020-09-25 20:16:16 +02:00
parent 5f3072a819
commit fbd761c8ee
12 changed files with 307 additions and 310 deletions

View file

@ -29,6 +29,7 @@
"@verdaccio/logger": "workspace:5.0.0-alpha.0",
"@verdaccio/utils": "workspace:5.0.0-alpha.0",
"@verdaccio/auth": "workspace:5.0.0-alpha.0",
"jsonwebtoken": "8.5.1",
"debug": "^4.1.1",
"express": "4.17.1",
"lodash": "4.17.15"

View file

@ -10,17 +10,6 @@ import {
} from '@verdaccio/commons-api';
import { API_ERROR, SUPPORT_ERRORS, TOKEN_BASIC, TOKEN_BEARER } from '@verdaccio/dev-commons';
import { loadPlugin } from '@verdaccio/loaders';
import {
aesEncrypt,
signPayload,
isNil,
isFunction,
getMatchedPackagesSpec,
getDefaultPlugins,
createAnonymousRemoteUser,
convertPayloadToBase64,
createRemoteUser,
} from '@verdaccio/utils';
import {
Config,
@ -35,9 +24,20 @@ import {
AllowAccess,
PackageAccess,
} from '@verdaccio/types';
import {
isNil,
isFunction,
getMatchedPackagesSpec,
createAnonymousRemoteUser,
convertPayloadToBase64,
createRemoteUser,
} from '@verdaccio/utils';
import {
getMiddlewareCredentials,
getSecurity,
getDefaultPlugins,
verifyJWTPayload,
parseBasicPayload,
parseAuthTokenHeader,
@ -45,6 +45,8 @@ import {
isAESLegacy,
} from './utils';
import { aesEncrypt, signPayload } from './crypto-utils';
/* eslint-disable @typescript-eslint/no-var-requires */
const LoggerApi = require('@verdaccio/logger');

View file

@ -0,0 +1,60 @@
import { createDecipher, createCipher } from 'crypto';
import jwt from 'jsonwebtoken';
import { JWTSignOptions, RemoteUser } from '@verdaccio/types';
export const defaultAlgorithm = 'aes192';
export function aesEncrypt(buf: Buffer, secret: string): Buffer {
// deprecated (it will be migrated in Verdaccio 5), it is a breaking change
// https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options
// https://www.grainger.xyz/changing-from-cipher-to-cipheriv/
const c = createCipher(defaultAlgorithm, secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
}
export function aesDecrypt(buf: Buffer, secret: string): Buffer {
try {
// deprecated (it will be migrated in Verdaccio 5), it is a breaking change
// https://nodejs.org/api/crypto.html#crypto_crypto_createdecipher_algorithm_password_options
// https://www.grainger.xyz/changing-from-cipher-to-cipheriv/
const c = createDecipher(defaultAlgorithm, secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
} catch (_) {
return new Buffer(0);
}
}
/**
* Sign the payload and return JWT
* https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback
* @param payload
* @param secretOrPrivateKey
* @param options
*/
export async function signPayload(
payload: RemoteUser,
secretOrPrivateKey: string,
options: JWTSignOptions = {}
): Promise<string> {
return new Promise(function (resolve, reject): Promise<string> {
return jwt.sign(
payload,
secretOrPrivateKey,
{
// 1 === 1ms (one millisecond)
notBefore: '1', // Make sure the time will not rollback :)
...options,
},
(error, token) => (error ? reject(error) : resolve(token))
);
});
}
export function verifyPayload(token: string, secretOrPrivateKey: string): RemoteUser {
return jwt.verify(token, secretOrPrivateKey);
}

View file

@ -1,2 +1,3 @@
export { Auth, IAuth, IAuthWebUI } from './auth';
export * from './utils';
export * from './crypto-utils';

View file

@ -1,17 +1,19 @@
import _ from 'lodash';
import { Config, RemoteUser, Security } from '@verdaccio/types';
import { HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER } from '@verdaccio/dev-commons';
import { Callback, Config, IPluginAuth, RemoteUser, Security } from '@verdaccio/types';
import { HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER, API_ERROR } from '@verdaccio/dev-commons';
import { getForbidden, getUnauthorized, getConflict, getCode } from '@verdaccio/commons-api';
import {
aesDecrypt,
AllowAction,
AllowActionCallback,
AuthPackageAllow,
buildUserBuffer,
convertPayloadToBase64,
createAnonymousRemoteUser,
defaultSecurity,
ErrorCode,
verifyPayload,
} from '@verdaccio/utils';
import { IAuthWebUI, AESPayload } from './auth';
import { aesDecrypt, verifyPayload } from './crypto-utils';
export type BasicPayload = AESPayload | void;
export type AuthMiddlewarePayload = RemoteUser | BasicPayload;
@ -127,7 +129,7 @@ export function verifyJWTPayload(token: string, secret: string): RemoteUser {
// we return an anonymous user to force log in.
return createAnonymousRemoteUser();
}
throw ErrorCode.getCode(HTTP_STATUS.UNAUTHORIZED, error.message);
throw getCode(HTTP_STATUS.UNAUTHORIZED, error.message);
}
}
@ -146,3 +148,78 @@ export function parseBasicPayload(credentials: string): BasicPayload {
return { user, password };
}
export function getDefaultPlugins(logger: any): IPluginAuth<Config> {
return {
authenticate(user: string, password: string, cb: Callback): void {
cb(getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));
},
adduser(user: string, password: string, cb: Callback): void {
return cb(getConflict(API_ERROR.BAD_USERNAME_PASSWORD));
},
// FIXME: allow_action and allow_publish should be in the @verdaccio/types
// @ts-ignore
allow_access: allow_action('access', logger),
// @ts-ignore
allow_publish: allow_action('publish', logger),
allow_unpublish: handleSpecialUnpublish(logger),
};
}
export type ActionsAllowed = 'publish' | 'unpublish' | 'access';
export function allow_action(action: ActionsAllowed, logger): AllowAction {
return function allowActionCallback(
user: RemoteUser,
pkg: AuthPackageAllow,
callback: AllowActionCallback
): void {
logger.trace({ remote: user.name }, `[auth/allow_action]: user: @{user.name}`);
const { name, groups } = user;
const groupAccess = pkg[action] as string[];
const hasPermission = groupAccess.some((group) => name === group || groups.includes(group));
logger.trace(
{ pkgName: pkg.name, hasPermission, remote: user.name, groupAccess },
`[auth/allow_action]: hasPermission? @{hasPermission} for user: @{user}`
);
if (hasPermission) {
logger.trace({ remote: user.name }, `auth/allow_action: access granted to: @{user}`);
return callback(null, true);
}
if (name) {
callback(getForbidden(`user ${name} is not allowed to ${action} package ${pkg.name}`));
} else {
callback(getUnauthorized(`authorization required to ${action} package ${pkg.name}`));
}
};
}
/**
*
*/
export function handleSpecialUnpublish(logger): any {
return function (user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {
const action = 'unpublish';
// verify whether the unpublish prop has been defined
const isUnpublishMissing: boolean = _.isNil(pkg[action]);
const hasGroups: boolean = isUnpublishMissing ? false : (pkg[action] as string[]).length > 0;
logger.trace(
{ user: user.name, name: pkg.name, hasGroups },
`fallback unpublish for @{name} has groups: @{hasGroups} for @{user}`
);
if (isUnpublishMissing || hasGroups === false) {
return callback(null, undefined);
}
logger.trace(
{ user: user.name, name: pkg.name, action, hasGroups },
`allow_action for @{action} for @{name} has groups: @{hasGroups} for @{user}`
);
return allow_action(action, logger)(user, pkg, callback);
};
}

View file

@ -1,6 +1,6 @@
import path from 'path';
import _ from 'lodash';
import { CHARACTER_ENCODING, TOKEN_BEARER } from '@verdaccio/dev-commons';
import { CHARACTER_ENCODING, TOKEN_BEARER, ROLES, API_ERROR } from '@verdaccio/dev-commons';
import { configExample } from '@verdaccio/mock';
import { Config as AppConfig } from '@verdaccio/config';
@ -9,19 +9,30 @@ import { setup } from '@verdaccio/logger';
import {
buildUserBuffer,
getAuthenticatedMessage,
aesDecrypt,
verifyPayload,
buildToken,
convertPayloadToBase64,
parseConfigFile,
createAnonymousRemoteUser,
createRemoteUser,
signPayload,
AllowActionCallbackResponse,
} from '@verdaccio/utils';
import { Config, Security, RemoteUser } from '@verdaccio/types';
import { Auth, IAuth } from '../src';
import { getMiddlewareCredentials, getApiToken, verifyJWTPayload, getSecurity } from '../src';
import { VerdaccioError, getForbidden } from '@verdaccio/commons-api';
import {
IAuth,
Auth,
ActionsAllowed,
allow_action,
getDefaultPlugins,
getMiddlewareCredentials,
getApiToken,
verifyJWTPayload,
getSecurity,
aesDecrypt,
verifyPayload,
signPayload,
} from '../src';
setup([]);
@ -86,6 +97,7 @@ describe('Auth utilities', () => {
const verifyAES = (token: string, user: string, password: string, secret: string) => {
const payload = aesDecrypt(convertPayloadToBase64(token), secret).toString(
// @ts-ignore
CHARACTER_ENCODING.UTF8
);
const content = payload.split(':');
@ -94,6 +106,120 @@ describe('Auth utilities', () => {
expect(content[0]).toBe(password);
};
describe('getDefaultPlugins', () => {
test('authentication should fail by default (default)', () => {
const plugin = getDefaultPlugins({ trace: jest.fn() });
plugin.authenticate('foo', 'bar', (error: any) => {
expect(error).toEqual(getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));
});
});
test('add user should fail by default (default)', () => {
const plugin = getDefaultPlugins({ trace: jest.fn() });
// @ts-ignore
plugin.adduser('foo', 'bar', (error: any) => {
expect(error).toEqual(getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));
});
});
});
describe('allow_action', () => {
describe('access/publish/unpublish and anonymous', () => {
const packageAccess = {
name: 'foo',
version: undefined,
access: ['foo'],
unpublish: false,
};
// const type = 'access';
test.each(['access', 'publish', 'unpublish'])(
'should restrict %s to anonymous users',
(type) => {
allow_action(type as ActionsAllowed, { trace: jest.fn() })(
createAnonymousRemoteUser(),
{
...packageAccess,
[type]: ['foo'],
},
(error: VerdaccioError | null, allowed: AllowActionCallbackResponse) => {
expect(error).not.toBeNull();
expect(allowed).toBeUndefined();
}
);
}
);
test.each(['access', 'publish', 'unpublish'])(
'should allow %s to anonymous users',
(type) => {
allow_action(type as ActionsAllowed, { trace: jest.fn() })(
createAnonymousRemoteUser(),
{
...packageAccess,
[type]: [ROLES.$ANONYMOUS],
},
(error: VerdaccioError | null, allowed: AllowActionCallbackResponse) => {
expect(error).toBeNull();
expect(allowed).toBe(true);
}
);
}
);
test.each(['access', 'publish', 'unpublish'])(
'should allow %s only if user is anonymous if the logged user has groups',
(type) => {
allow_action(type as ActionsAllowed, { trace: jest.fn() })(
createRemoteUser('juan', ['maintainer', 'admin']),
{
...packageAccess,
[type]: [ROLES.$ANONYMOUS],
},
(error: VerdaccioError | null, allowed: AllowActionCallbackResponse) => {
expect(error).not.toBeNull();
expect(allowed).toBeUndefined();
}
);
}
);
test.each(['access', 'publish', 'unpublish'])(
'should allow %s only if user is anonymous match any other groups',
(type) => {
allow_action(type as ActionsAllowed, { trace: jest.fn() })(
createRemoteUser('juan', ['maintainer', 'admin']),
{
...packageAccess,
[type]: ['admin', 'some-other-group', ROLES.$ANONYMOUS],
},
(error: VerdaccioError | null, allowed: AllowActionCallbackResponse) => {
expect(error).toBeNull();
expect(allowed).toBe(true);
}
);
}
);
test.each(['access', 'publish', 'unpublish'])(
'should not allow %s anonymous if other groups are defined and does not match',
(type) => {
allow_action(type as ActionsAllowed, { trace: jest.fn() })(
createRemoteUser('juan', ['maintainer', 'admin']),
{
...packageAccess,
[type]: ['bla-bla-group', 'some-other-group', ROLES.$ANONYMOUS],
},
(error: VerdaccioError | null, allowed: AllowActionCallbackResponse) => {
expect(error).not.toBeNull();
expect(allowed).toBeUndefined();
}
);
}
);
});
});
describe('getApiToken test', () => {
test('should sign token with aes and security missing', async () => {
const token = await signCredentials(

View file

@ -1,4 +1,5 @@
import { aesDecrypt, aesEncrypt, convertPayloadToBase64 } from '../src';
import { convertPayloadToBase64 } from '@verdaccio/utils';
import { aesDecrypt, aesEncrypt } from '../src/crypto-utils';
describe('test crypto utils', () => {
describe('default encryption', () => {

View file

@ -19,7 +19,6 @@
"@verdaccio/dev-commons": "workspace:5.0.0-alpha.0",
"@verdaccio/readme": "workspace:*",
"js-yaml": "3.13.1",
"jsonwebtoken": "8.5.1",
"minimatch": "3.0.4",
"semver": "7.3.2"
},

View file

@ -1,26 +1,14 @@
import _ from 'lodash';
import {
API_ERROR,
ROLES,
TIME_EXPIRATION_7D,
DEFAULT_MIN_LIMIT_PASSWORD,
} from '@verdaccio/dev-commons';
import { ROLES, TIME_EXPIRATION_7D, DEFAULT_MIN_LIMIT_PASSWORD } from '@verdaccio/dev-commons';
import {
RemoteUser,
AllowAccess,
PackageAccess,
Callback,
Config,
Security,
APITokenOptions,
JWTOptions,
IPluginAuth,
} from '@verdaccio/types';
import { VerdaccioError } from '@verdaccio/commons-api';
import { ErrorCode } from './utils';
export interface CookieSessionToken {
expires: Date;
}
@ -85,95 +73,18 @@ export type AllowActionCallback = (
error: VerdaccioError | null,
allowed?: AllowActionCallbackResponse
) => void;
export type AllowAction = (
user: RemoteUser,
pkg: AuthPackageAllow,
callback: AllowActionCallback
) => void;
export interface AuthPackageAllow extends PackageAccess, AllowAccess {
// TODO: this should be on @verdaccio/types
unpublish: boolean | string[];
}
export type ActionsAllowed = 'publish' | 'unpublish' | 'access';
export function allow_action(action: ActionsAllowed, logger): AllowAction {
return function allowActionCallback(
user: RemoteUser,
pkg: AuthPackageAllow,
callback: AllowActionCallback
): void {
logger.trace({ remote: user.name }, `[auth/allow_action]: user: @{user.name}`);
const { name, groups } = user;
const groupAccess = pkg[action] as string[];
const hasPermission = groupAccess.some((group) => name === group || groups.includes(group));
logger.trace(
{ pkgName: pkg.name, hasPermission, remote: user.name, groupAccess },
`[auth/allow_action]: hasPermission? @{hasPermission} for user: @{user}`
);
if (hasPermission) {
logger.trace({ remote: user.name }, `auth/allow_action: access granted to: @{user}`);
return callback(null, true);
}
if (name) {
callback(
ErrorCode.getForbidden(`user ${name} is not allowed to ${action} package ${pkg.name}`)
);
} else {
callback(
ErrorCode.getUnauthorized(`authorization required to ${action} package ${pkg.name}`)
);
}
};
}
/**
*
*/
export function handleSpecialUnpublish(logger): any {
return function (user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {
const action = 'unpublish';
// verify whether the unpublish prop has been defined
const isUnpublishMissing: boolean = _.isNil(pkg[action]);
const hasGroups: boolean = isUnpublishMissing ? false : (pkg[action] as string[]).length > 0;
logger.trace(
{ user: user.name, name: pkg.name, hasGroups },
`fallback unpublish for @{name} has groups: @{hasGroups} for @{user}`
);
if (isUnpublishMissing || hasGroups === false) {
return callback(null, undefined);
}
logger.trace(
{ user: user.name, name: pkg.name, action, hasGroups },
`allow_action for @{action} for @{name} has groups: @{hasGroups} for @{user}`
);
return allow_action(action, logger)(user, pkg, callback);
};
}
export function getDefaultPlugins(logger: any): IPluginAuth<Config> {
return {
authenticate(user: string, password: string, cb: Callback): void {
cb(ErrorCode.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));
},
adduser(user: string, password: string, cb: Callback): void {
return cb(ErrorCode.getConflict(API_ERROR.BAD_USERNAME_PASSWORD));
},
// FIXME: allow_action and allow_publish should be in the @verdaccio/types
// @ts-ignore
allow_access: allow_action('access', logger),
// @ts-ignore
allow_publish: allow_action('publish', logger),
allow_unpublish: handleSpecialUnpublish(logger),
};
}
export function createSessionToken(): CookieSessionToken {
const tenHoursTime = 10 * 60 * 60 * 1000;

View file

@ -1,35 +1,8 @@
import { createDecipher, createCipher, createHash, pseudoRandomBytes, Hash } from 'crypto';
import jwt from 'jsonwebtoken';
import { createHash, pseudoRandomBytes, Hash } from 'crypto';
import { JWTSignOptions, RemoteUser } from '@verdaccio/types';
export const defaultAlgorithm = 'aes192';
export const defaultTarballHashAlgorithm = 'sha1';
export function aesEncrypt(buf: Buffer, secret: string): Buffer {
// deprecated (it will be migrated in Verdaccio 5), it is a breaking change
// https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options
// https://www.grainger.xyz/changing-from-cipher-to-cipheriv/
const c = createCipher(defaultAlgorithm, secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
}
export function aesDecrypt(buf: Buffer, secret: string): Buffer {
try {
// deprecated (it will be migrated in Verdaccio 5), it is a breaking change
// https://nodejs.org/api/crypto.html#crypto_crypto_createdecipher_algorithm_password_options
// https://www.grainger.xyz/changing-from-cipher-to-cipheriv/
const c = createDecipher(defaultAlgorithm, secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
} catch (_) {
return Buffer.alloc(0);
}
}
// podria moverse a storage donde se usa
export function createTarballHash(): Hash {
return createHash(defaultTarballHashAlgorithm);
}
@ -41,40 +14,12 @@ export function createTarballHash(): Hash {
* @param {Object} data
* @return {String}
*/
// se usa en api, middleware, web
export function stringToMD5(data: Buffer | string): string {
return createHash('md5').update(data).digest('hex');
}
// se usa en config
export function generateRandomHexString(length = 8): string {
return pseudoRandomBytes(length).toString('hex');
}
/**
* Sign the payload and return JWT
* https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback
* @param payload
* @param secretOrPrivateKey
* @param options
*/
export async function signPayload(
payload: RemoteUser,
secretOrPrivateKey: string,
options: JWTSignOptions = {}
): Promise<string> {
return new Promise(function (resolve, reject): Promise<string> {
return jwt.sign(
payload,
secretOrPrivateKey,
{
// 1 === 1ms (one millisecond)
notBefore: '1', // Make sure the time will not rollback :)
...options,
},
(error, token) => (error ? reject(error) : resolve(token))
);
});
}
export function verifyPayload(token: string, secretOrPrivateKey: string): RemoteUser {
return jwt.verify(token, secretOrPrivateKey);
}

View file

@ -1,16 +1,13 @@
import { API_ERROR, ROLES } from '@verdaccio/dev-commons';
import { VerdaccioError, getForbidden } from '@verdaccio/commons-api';
import {
allow_action,
createAnonymousRemoteUser,
createRemoteUser,
validatePassword,
ActionsAllowed,
AllowActionCallbackResponse,
getDefaultPlugins,
createSessionToken,
getAuthenticatedMessage,
} from '../src';
jest.mock('@verdaccio/logger', () => ({
logger: { trace: jest.fn() },
}));
@ -60,102 +57,6 @@ describe('Auth Utilities', () => {
});
});
describe('allow_action', () => {
describe('access/publish/unpublish and anonymous', () => {
const packageAccess = {
name: 'foo',
version: undefined,
access: ['foo'],
unpublish: false,
};
// const type = 'access';
test.each(['access', 'publish', 'unpublish'])(
'should restrict %s to anonymous users',
(type) => {
allow_action(type as ActionsAllowed, { trace: jest.fn() })(
createAnonymousRemoteUser(),
{
...packageAccess,
[type]: ['foo'],
},
(error: VerdaccioError | null, allowed: AllowActionCallbackResponse) => {
expect(error).not.toBeNull();
expect(allowed).toBeUndefined();
}
);
}
);
test.each(['access', 'publish', 'unpublish'])(
'should allow %s to anonymous users',
(type) => {
allow_action(type as ActionsAllowed, { trace: jest.fn() })(
createAnonymousRemoteUser(),
{
...packageAccess,
[type]: [ROLES.$ANONYMOUS],
},
(error: VerdaccioError | null, allowed: AllowActionCallbackResponse) => {
expect(error).toBeNull();
expect(allowed).toBe(true);
}
);
}
);
test.each(['access', 'publish', 'unpublish'])(
'should allow %s only if user is anonymous if the logged user has groups',
(type) => {
allow_action(type as ActionsAllowed, { trace: jest.fn() })(
createRemoteUser('juan', ['maintainer', 'admin']),
{
...packageAccess,
[type]: [ROLES.$ANONYMOUS],
},
(error: VerdaccioError | null, allowed: AllowActionCallbackResponse) => {
expect(error).not.toBeNull();
expect(allowed).toBeUndefined();
}
);
}
);
test.each(['access', 'publish', 'unpublish'])(
'should allow %s only if user is anonymous match any other groups',
(type) => {
allow_action(type as ActionsAllowed, { trace: jest.fn() })(
createRemoteUser('juan', ['maintainer', 'admin']),
{
...packageAccess,
[type]: ['admin', 'some-other-group', ROLES.$ANONYMOUS],
},
(error: VerdaccioError | null, allowed: AllowActionCallbackResponse) => {
expect(error).toBeNull();
expect(allowed).toBe(true);
}
);
}
);
test.each(['access', 'publish', 'unpublish'])(
'should not allow %s anonymous if other groups are defined and does not match',
(type) => {
allow_action(type as ActionsAllowed, { trace: jest.fn() })(
createRemoteUser('juan', ['maintainer', 'admin']),
{
...packageAccess,
[type]: ['bla-bla-group', 'some-other-group', ROLES.$ANONYMOUS],
},
(error: VerdaccioError | null, allowed: AllowActionCallbackResponse) => {
expect(error).not.toBeNull();
expect(allowed).toBeUndefined();
}
);
}
);
});
});
describe('createSessionToken', () => {
test('should generate session token', () => {
expect(createSessionToken()).toHaveProperty('expires');
@ -163,23 +64,6 @@ describe('Auth Utilities', () => {
});
});
describe('getDefaultPlugins', () => {
test('authentication should fail by default (default)', () => {
const plugin = getDefaultPlugins({ trace: jest.fn() });
plugin.authenticate('foo', 'bar', (error: any) => {
expect(error).toEqual(getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));
});
});
test('add user should fail by default (default)', () => {
const plugin = getDefaultPlugins({ trace: jest.fn() });
// @ts-ignore
plugin.adduser('foo', 'bar', (error: any) => {
expect(error).toEqual(getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));
});
});
});
describe('getAuthenticatedMessage', () => {
test('should generate user message token', () => {
expect(getAuthenticatedMessage('foo')).toEqual("you are authenticated as 'foo'");

View file

@ -224,6 +224,7 @@ importers:
'@verdaccio/utils': 'link:../utils'
debug: 4.1.1
express: 4.17.1
jsonwebtoken: 8.5.1
lodash: 4.17.15
devDependencies:
'@verdaccio/config': 'link:../config'
@ -241,6 +242,7 @@ importers:
'@verdaccio/utils': 'workspace:5.0.0-alpha.0'
debug: ^4.1.1
express: 4.17.1
jsonwebtoken: 8.5.1
lodash: 4.17.15
packages/cli:
dependencies:
@ -622,7 +624,6 @@ importers:
'@verdaccio/dev-commons': 'link:../commons'
'@verdaccio/readme': 'link:../core/readme'
js-yaml: 3.13.1
jsonwebtoken: 8.5.1
minimatch: 3.0.4
semver: 7.3.2
devDependencies:
@ -636,7 +637,6 @@ importers:
'@verdaccio/logger': 'workspace:5.0.0-alpha.0'
'@verdaccio/readme': 'workspace:*'
js-yaml: 3.13.1
jsonwebtoken: 8.5.1
lodash: ^4.17.20
minimatch: 3.0.4
semver: 7.3.2
@ -940,7 +940,7 @@ packages:
/@babel/helper-compilation-targets/7.10.4:
dependencies:
'@babel/compat-data': 7.11.0
browserslist: 4.14.0
browserslist: 4.14.4
invariant: 2.2.4
levenary: 1.1.1
semver: 5.7.1
@ -993,7 +993,7 @@ packages:
dependencies:
'@babel/helper-annotate-as-pure': 7.10.4
'@babel/helper-regex': 7.10.5
regexpu-core: 4.7.0
regexpu-core: 4.7.1
dev: false
peerDependencies:
'@babel/core': ^7.0.0
@ -1339,8 +1339,8 @@ packages:
dependencies:
'@babel/core': 7.10.5
'@babel/helper-plugin-utils': 7.10.4
'@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.10.5
'@babel/plugin-transform-parameters': 7.10.5_@babel+core@7.10.5
'@babel/plugin-syntax-object-rest-spread': 7.8.3
'@babel/plugin-transform-parameters': 7.10.5
dev: false
peerDependencies:
'@babel/core': ^7.0.0-0
@ -1732,6 +1732,7 @@ packages:
dependencies:
'@babel/core': 7.10.5
'@babel/helper-plugin-utils': 7.10.4
dev: true
peerDependencies:
'@babel/core': ^7.0.0-0
resolution:
@ -2251,16 +2252,6 @@ packages:
'@babel/core': ^7.0.0-0
resolution:
integrity: sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==
/@babel/plugin-transform-parameters/7.10.5_@babel+core@7.10.5:
dependencies:
'@babel/core': 7.10.5
'@babel/helper-get-function-arity': 7.10.4
'@babel/helper-plugin-utils': 7.10.4
dev: false
peerDependencies:
'@babel/core': ^7.0.0-0
resolution:
integrity: sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==
/@babel/plugin-transform-parameters/7.10.5_@babel+core@7.11.6:
dependencies:
'@babel/core': 7.11.6
@ -18839,7 +18830,6 @@ packages:
regjsparser: 0.6.4
unicode-match-property-ecmascript: 1.0.4
unicode-match-property-value-ecmascript: 1.2.0
dev: true
engines:
node: '>=4'
resolution: