@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
95 lines (84 loc) • 3.54 kB
JavaScript
;
const BadRequestError = require('../utils/errors/http/badRequest');
const xsenv = require('@sap/xsenv');
const xssec = require('@sap/xssec');
/**
* Validates and extracts the authorization token from OData context without prefixes.
* According to RFC-6750, the HTTP request's authorization header must look like:
* "Bearer <JWT-Token>".
* @param context {Object} The OData context of the framework.
* @returns {string} "<JWT-Token>".
*/
exports.getAuthToken = function getAuthToken(context) {
const authorizationHeader =
context.request.headers.Authorization ||
context.request.headers.authorization;
if (!authorizationHeader) {
throw new BadRequestError(
'Missing Authorization request header',
context
);
}
// see RFC6750, section 2.1, correct bearer header parsing
const matches = authorizationHeader.match(/^(?:bearer)(?:\s)+(\S*)$/i);
if (matches === null || matches.length !== 2) {
throw new BadRequestError('Bad Authorization request header', context);
}
return matches[1];
};
/**
* Checks the collected scopes against the given token. It uses the xssec module to check the scopes
* one by one. A scope name must be given in the xsodata corresponding file WITHOUT the app name, because
* checkLocalScope appends the app name automatically.
* @param token {string} Authorization token extracted from the HTTP header.
* @param scopes {Array<String>} Collection of scopes from all strategies.
* @param callback {Function} Callback to be called when scopes checks are done.
*/
exports.checkScopes = function checkScopes(context, token, scopes, callback) {
// The UAA service is read (using xsenv module) from the default-services.json file at root-dir
const uaaService =
xsenv.getServices({ uaa: { tag: 'xsuaa' } }).uaa ||
xsenv.getServices({ uaa: 'uaa' }).uaa;
xssec.createSecurityContext(
token,
uaaService,
function (err, externalSecurityContext) {
if (err) {
context.securityContext = externalSecurityContext;
return context.callRegisteredStep(
9,
'CreateSecurityContext creation failed',
context,
() => {
if (err === false) {
// would interfere with async cancel water fall, error should be an error
return new Error(
'Security context could not be created'
);
}
return callback(err, null);
}
);
}
let isAuthorized = true;
scopes.forEach(function (scope) {
if (externalSecurityContext.checkLocalScope(scope) !== true) {
isAuthorized = false;
}
});
if (!isAuthorized && context && context.callRegisteredStep) {
context.securityContext = externalSecurityContext;
return context.callRegisteredStep(
9,
'Not Authorized (scope check failed)',
context,
() => {
return callback(null, isAuthorized);
}
);
} else {
return callback(null, isAuthorized);
}
}
);
};