0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-02-03 23:09:17 -05:00
verdaccio/packages/utils/src/auth-utils.ts

159 lines
5.5 KiB
TypeScript
Raw Normal View History

import _ from 'lodash';
2020-03-03 23:59:19 +01:00
2020-03-08 10:13:56 +01:00
import { API_ERROR, ROLES, TIME_EXPIRATION_7D, DEFAULT_MIN_LIMIT_PASSWORD } from '@verdaccio/dev-commons';
import { CookieSessionToken, AuthTokenHeader, BasicPayload } from '@verdaccio/dev-types';
2020-03-08 09:19:12 +01:00
import { RemoteUser, AllowAccess, PackageAccess, Callback, Config, Security, APITokenOptions, JWTOptions, IPluginAuth } from '@verdaccio/types';
import { VerdaccioError } from '@verdaccio/commons-api';
2020-03-03 23:59:19 +01:00
2020-03-08 10:13:56 +01:00
import { ErrorCode } from './utils';
2020-03-03 23:59:19 +01:00
export function validatePassword(password: string, minLength: number = DEFAULT_MIN_LIMIT_PASSWORD): boolean {
return typeof password === 'string' && password.length >= minLength;
}
2020-03-08 09:19:12 +01:00
/**
* All logged users will have by default the group $all and $authenticate
*/
export const defaultLoggedUserRoles = [ROLES.$ALL, ROLES.$AUTH, ROLES.DEPRECATED_ALL, ROLES.DEPRECATED_AUTH, ROLES.ALL];
/**
*
*/
export const defaultNonLoggedUserRoles = [
ROLES.$ALL,
ROLES.$ANONYMOUS,
// groups without '$' are going to be deprecated eventually
ROLES.DEPRECATED_ALL,
ROLES.DEPRECATED_ANONYMOUS
];
/**
* Create a RemoteUser object
* @return {Object} { name: xx, pluginGroups: [], real_groups: [] }
*/
export function createRemoteUser(name: string, pluginGroups: string[]): RemoteUser {
2018-09-21 17:34:12 +02:00
const isGroupValid: boolean = Array.isArray(pluginGroups);
2020-03-08 09:19:12 +01:00
const groups = (isGroupValid ? pluginGroups : []).concat([...defaultLoggedUserRoles]);
return {
name,
groups,
real_groups: pluginGroups,
};
}
/**
* Builds an anonymous remote user in case none is logged in.
* @return {Object} { name: xx, groups: [], real_groups: [] }
*/
export function createAnonymousRemoteUser(): RemoteUser {
return {
name: undefined,
2020-03-08 09:19:12 +01:00
groups: [...defaultNonLoggedUserRoles],
real_groups: [],
};
}
2018-07-17 20:33:51 +02:00
2020-03-08 09:19:12 +01:00
export type AllowActionCallbackResponse = boolean | undefined;
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 {
2020-03-08 09:19:12 +01:00
return function allowActionCallback(user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {
2020-03-03 23:59:19 +01:00
logger.trace({remote: user.name}, `[auth/allow_action]: user: @{user.name}`);
const { name, groups } = user;
2020-03-08 09:19:12 +01:00
const groupAccess = pkg[action] as string[];
2020-03-03 23:59:19 +01:00
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}`);
2018-07-15 00:30:47 +02:00
if (hasPermission) {
2020-03-03 23:59:19 +01:00
logger.trace({remote: user.name}, `auth/allow_action: access granted to: @{user}`);
2018-07-15 00:30:47 +02:00
return callback(null, true);
}
2018-07-15 00:30:47 +02:00
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 {
2020-03-08 09:19:12 +01:00
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]);
2020-03-08 09:19:12 +01:00
const hasGroups: boolean = isUnpublishMissing ? false : ((pkg[action]) as string[]).length > 0;
2020-03-03 23:59:19 +01:00
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);
}
2020-03-03 23:59:19 +01:00
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 {
2020-03-03 23:59:19 +01:00
authenticate(user: string, password: string, cb: Callback): void {
cb(ErrorCode.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));
},
2020-03-08 09:19:12 +01:00
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;
return {
// npmjs.org sets 10h expire
expires: new Date(Date.now() + tenHoursTime),
};
}
const defaultWebTokenOptions: JWTOptions = {
sign: {
// The expiration token for the website is 7 days
expiresIn: TIME_EXPIRATION_7D,
},
verify: {},
};
const defaultApiTokenConf: APITokenOptions = {
legacy: true,
};
export const defaultSecurity: Security = {
web: defaultWebTokenOptions,
api: defaultApiTokenConf,
};
export function getAuthenticatedMessage(user: string): string {
return `you are authenticated as '${user}'`;
}
export function buildUserBuffer(name: string, password: string): Buffer {
return Buffer.from(`${name}:${password}`, 'utf8');
}