UNPKG

@universis/janitor

Version:

Universis api plugin for handling user authorization and rate limiting

385 lines (342 loc) 14 kB
import * as _themost_common from '@themost/common'; import { ApplicationService, ApplicationBase, ConfigurationStrategy, ConfigurationBase, HttpForbiddenError, HttpError } from '@themost/common'; import { Router, Application, Handler, Request as Request$1 } from 'express'; import { Store, Options } from 'express-rate-limit'; import { BehaviorSubject } from 'rxjs'; import { Store as Store$1, Options as Options$1 } from 'express-slow-down'; import RedisStore from 'rate-limit-redis'; import { DataContext } from '@themost/data'; import BearerStrategy from 'passport-http-bearer'; import { Authenticator } from 'passport'; declare type RateLimitStoreConstructor = new (...arg: unknown[]) => Store; declare interface RateLimitServiceConfiguration { extends?: string; storeType?: string; profiles?: [string, Options][]; paths?: [string, ({ profile: string } | Options)][]; } declare class RateLimitService extends ApplicationService { constructor(app: _themost_common.ApplicationBase); readonly loaded: BehaviorSubject<Router>; getServiceContainer(): BehaviorSubject<Router | Application>; getServiceName(): string; getServiceConfiguration(): RateLimitServiceConfiguration; set(path: string, options: { profile?: string } | Options): this; unset(path: string): this; } declare type SpeedLimitStoreConstructor = new (...arg: unknown[]) => Store$1; declare interface SpeedLimitServiceConfiguration { extends?: string; storeType?: string; profiles?: Map<string, Options$1>; paths?: Map<string, ({ profile: string } | Options$1)>; } declare class SpeedLimitService extends ApplicationService { constructor(app: ApplicationBase); getServiceContainer(): BehaviorSubject<Router | Application>; getServiceName(): string; getServiceConfiguration(): SpeedLimitServiceConfiguration; set(path: string, options: { profile?: string } | Options$1): this; unset(path: string): this; } declare class RedisClientStore extends RedisStore { constructor(app: ApplicationService); } /** * @license * Universis Project Version 1.0 * Copyright (c) 2018, Universis Project All rights reserved * * Use of this source code is governed by an LGPL 3.0 license that can be * found in the LICENSE file at https://universis.io/license */ declare class ScopeString { constructor(str: string); split(): string[]; } declare interface UniversisConfigurationSection { universis: { [k: string]: any; } } declare interface ScopeAccessConfigurationSection { janitor: { scopeAccess: { imports: string[] } } } /** * Declares a configuration element for managing scope-based permissions on server resources */ declare interface ScopeAccessConfigurationElement { /** * Gets or sets an array of strings that holds an array of scopes e.g. students or students:read or students,teachers etc */ scope: string[], /** * Gets or sets a string which represents the regular expression that is going to be used for validating endpoint */ resource: string; /** * Gets or sets an array of strings which represents the access levels for the given scopes e.g. READ or READ,WRITE etc */ access: string[]; /** * Gets or sets a string which represents a short description for this item */ description?: string; } declare class ScopeAccessConfiguration extends ConfigurationStrategy { constructor(configuration: ConfigurationBase); /** * Gets an array of scope access configuration elements */ public elements: ScopeAccessConfigurationElement[]; /** * Verifies the given request and returns a promise that resolves with a scope access configuration element */ verify(req: Request): Promise<ScopeAccessConfigurationElement>; } declare class DefaultScopeAccessConfiguration extends ScopeAccessConfiguration { constructor(configuration: ConfigurationBase); /** * Gets an array of scope access configuration elements */ public elements: ScopeAccessConfigurationElement[]; /** * Verifies the given request and returns a promise that resolves with a scope access configuration element */ verify(req: Request): Promise<ScopeAccessConfigurationElement>; } declare class EnableScopeAccessConfiguration extends ApplicationService { constructor(app: ApplicationBase); } declare class ExtendScopeAccessConfiguration extends ApplicationService { constructor(app: ApplicationBase); } declare function validateScope(): Handler; declare interface OAuth2MethodOptions { access_token: string; } declare interface OAuth2AuthorizeUser { client_id?: string; client_secret?: string; username: string; password: string; grant_type: string; scope?: string; } declare interface OAuth2ServiceSettings { unattendedExecutionAccount?: string; client_id: string; client_secret?: string; server_uri: string; userinfo_uri?: string; introspect_uri?: string; admin_uri?: string; well_known_configuration_uri?: string; adminAccount: { username: string; password: string; client_id: string; client_secret?: string; scope?: string; } } declare interface OAuth2UserProfile { sub: string; name: string; preferred_username: string; given_name: string; family_name: string; email: string; } declare interface GenericUser { id?: any; additionalType?: string; alternateName?: string; description?: string; givenName?: string; familyName?: string; image?: string; name?: string; url?: string; dateCreated?: Date; dateModified?: Date; createdBy?: any; modifiedBy?: any; lockoutTime?: Date; logonCount?: number; enabled?: boolean; lastLogon?: Date; userCredentials?: { userPassword?: string; userActivated?: boolean; temporary?: boolean; } } declare interface OAuth2User { id?: any; username?: string; email?: string; enabled?: boolean; emailVerified?: boolean; firstName?: string; lastName?: string; credentials?: { algorithm?: string, temporary?: boolean, type?: string, value?: string } } declare class OAuth2ClientService extends ApplicationService { get settings(): OAuth2ServiceSettings; constructor(app: ApplicationBase) getUserInfo(context: DataContext, token: string): Promise<OAuth2UserProfile>; getTokenInfo(context: DataContext, token: string): Promise<any>; getContextTokenInfo(context: DataContext): Promise<any>; authorize(authorizeUser: OAuth2AuthorizeUser): Promise<{ access_token?: string, refresh_token?: string}>; getUser(username: string, options: OAuth2MethodOptions): Promise<any>; getUserById(user_id: any, options: OAuth2MethodOptions): Promise<any>; getUserByEmail(email: string, options: OAuth2MethodOptions): Promise<any>; updateUser(user: GenericUser | any, options: OAuth2MethodOptions): Promise<any>; createUser(user: GenericUser | any, options: OAuth2MethodOptions): Promise<any>; deleteUser(user: { id: any }, options: OAuth2MethodOptions): Promise<any>; } declare class RemoteAddressValidator extends ApplicationService { constructor(app: ApplicationService); validateRemoteAddress(request: Request$1): Promise<boolean>; getRemoteAddress(request: Request$1): string; } declare class AppRateLimitService extends RateLimitService { constructor(app: _themost_common.ApplicationBase); } declare class AppSpeedLimitService extends SpeedLimitService { constructor(app: _themost_common.ApplicationBase); } declare class HttpBearerTokenRequired extends HttpError { constructor() { super(499, 'A token is required to fulfill the request.'); this.code = 'E_TOKEN_REQUIRED'; this.title = 'Token Required'; } } declare class HttpBearerTokenNotFound extends HttpError { constructor() { super(498, 'Token was not found.'); this.code = 'E_TOKEN_NOT_FOUND'; this.title = 'Invalid token'; } } declare class HttpBearerTokenExpired extends HttpError { constructor() { super(498, 'Token was expired or is in invalid state.'); this.code = 'E_TOKEN_EXPIRED'; this.title = 'Invalid token'; } } declare class HttpAccountDisabled extends HttpForbiddenError { constructor() { super('Access is denied. User account is disabled.'); this.code = 'E_ACCOUNT_DISABLED'; this.statusCode = 403.2; this.title = 'Disabled account'; } } declare class HttpBearerStrategy extends BearerStrategy { constructor() { super({ passReqToCallback: true }, /** * @param {Request} req * @param {string} token * @param {Function} done */ function(req, token, done) { /** * Gets OAuth2 client services * @type {import('./OAuth2ClientService').OAuth2ClientService} */ let client = req.context.getApplication().getStrategy(function OAuth2ClientService() {}); // if client cannot be found if (client == null) { // throw configuration error return done(new Error('Invalid application configuration. OAuth2 client service cannot be found.')) } if (token == null) { // throw 499 Token Required error return done(new HttpBearerTokenRequired()); } // get token info client.getTokenInfo(req.context, token).then(info => { if (info == null) { // the specified token cannot be found - 498 invalid token with specific code return done(new HttpBearerTokenNotFound()); } // if the given token is not active throw token expired - 498 invalid token with specific code if (!info.active) { return done(new HttpBearerTokenExpired()); } // find user from token info return (function () { /** * @type {import('./services/user-provisioning-mapper-service').UserProvisioningMapperService} */ const mapper = req.context.getApplication().getService(function UserProvisioningMapperService() {}); if (mapper == null) { return req.context.model('User').where('name').equal(info.username).silent().getItem(); } return mapper.getUser(req.context, info); })().then((user) => { // check if userProvisioning service is installed and try to find related user only if user not found if (user == null) { /** * @type {import('./services/user-provisioning-service').UserProvisioningService} */ const service = req.context.getApplication().getService(function UserProvisioningService() {}); if (service == null) { return user; } return service.validateUser(req.context, info); } return user; }).then( user => { // user cannot be found and of course cannot be authenticated (throw forbidden error) if (user == null) { // write access log for forbidden return done(new HttpForbiddenError()); } // check if user has enabled attribute if (Object.prototype.hasOwnProperty.call(user, 'enabled') && !user.enabled) { //if user.enabled is off throw forbidden error return done(new HttpAccountDisabled('Access is denied. User account is disabled.')); } // otherwise return user data return done(null, { 'name': user.name, 'authenticationProviderKey': user.id, 'authenticationType':'Bearer', 'authenticationToken': token, 'authenticationScope': info.scope }); }); }).catch(err => { // end log token info request with error if (err && err.statusCode === 404) { // revert 404 not found returned by auth server to 498 invalid token return done(new HttpBearerTokenNotFound()); } // otherwise continue with error return done(err); }); }); } } declare class PassportService extends ApplicationService { getInstance(): Authenticator } export { AppRateLimitService, AppSpeedLimitService, DefaultScopeAccessConfiguration, EnableScopeAccessConfiguration, ExtendScopeAccessConfiguration, HttpAccountDisabled, HttpBearerStrategy, HttpBearerTokenExpired, HttpBearerTokenNotFound, HttpBearerTokenRequired, OAuth2ClientService, PassportService, RateLimitService, RedisClientStore, RemoteAddressValidator, ScopeAccessConfiguration, ScopeString, SpeedLimitService, validateScope }; export type { GenericUser, OAuth2AuthorizeUser, OAuth2MethodOptions, OAuth2ServiceSettings, OAuth2User, OAuth2UserProfile, RateLimitServiceConfiguration, RateLimitStoreConstructor, ScopeAccessConfigurationElement, ScopeAccessConfigurationSection, SpeedLimitServiceConfiguration, SpeedLimitStoreConstructor, UniversisConfigurationSection };