UNPKG

@universis/janitor

Version:

Universis api plugin for handling user authorization and rate limiting

134 lines (128 loc) 5.51 kB
import { HttpError, HttpForbiddenError } from '@themost/common'; import BearerStrategy from 'passport-http-bearer'; class HttpBearerTokenRequired extends HttpError { constructor() { super(499, 'A token is required to fulfill the request.'); this.code = 'E_TOKEN_REQUIRED'; this.title = 'Token Required'; } } class HttpBearerTokenNotFound extends HttpError { constructor() { super(498, 'Token was not found.'); this.code = 'E_TOKEN_NOT_FOUND'; this.title = 'Invalid token'; } } class HttpBearerTokenExpired extends HttpError { constructor() { super(498, 'Token was expired or is in invalid state.'); this.code = 'E_TOKEN_EXPIRED'; this.title = 'Invalid token'; } } 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'; } } 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); }); }); } } export { HttpAccountDisabled, HttpBearerTokenExpired, HttpBearerTokenNotFound, HttpBearerTokenRequired, HttpBearerStrategy }