@universis/janitor
Version:
Universis api plugin for handling user authorization and rate limiting
134 lines (128 loc) • 5.51 kB
JavaScript
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.getA$1pplication().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
}