SAP-BTP-Spielwiese/app1/node_modules/@sap/xssec/lib/ctx/xsuaa.js

371 lines
12 KiB
JavaScript
Raw Normal View History

'use strict';
const constants = require('../constants');
const requests = require('../requests');
const {JwtTokenValidatorXSUAA} = require('../validator')
// use environment variable DEBUG with value 'xssec:*' for trace/error messages
var debug = require('debug');
var debugTrace = debug('xssec:securitycontext');
var debugError = debug('xssec:securitycontext');
debugError.log = console.error.bind(console);
debugTrace.log = console.log.bind(console);
module.exports.SecurityContext = function(config, configArr) {
var userInfo = {
logonName: '',
givenName: '',
familyName: '',
email: ''
};
const xsappname = config.xsappname;
var token;
var scopes;
var samlToken;
var clientId;
var subaccountid;
var zid;
var subdomain = null;
var origin = null;
var userAttributes;
var additionalAuthAttributes;
var serviceinstanceid = null;
var grantType;
var expirationDate;
var tokenInfo = null;
var isForeignMode = false;
this.getConfigType = function () {
return "XSUAA";
}
function ifNotClientCredentialsToken(functionName, value) {
if (grantType === constants.GRANTTYPE_CLIENTCREDENTIAL) {
var errorString = '\nCall to ' + functionName + ' not allowed with a token of grant type ' + constants.GRANTTYPE_CLIENTCREDENTIAL + '.';
debugTrace(errorString);
return null;
}
return value;
}
this.getSubaccountId = function () {
return subaccountid;
};
this.getZoneId = function () {
return zid;
};
this.getAppTID = function () {
return zid;
};
this.getSubdomain = function () {
return subdomain;
};
this.getClientId = function () {
return clientId;
};
this.getExpirationDate = function () {
return expirationDate;
};
this.getOrigin = function () {
return origin;
};
this.getLogonName = function () {
return ifNotClientCredentialsToken('SecurityContext.getLogonName', userInfo.logonName);
};
this.getGivenName = function () {
return ifNotClientCredentialsToken('SecurityContext.getGivenName', userInfo.givenName);
};
this.getFamilyName = function () {
return ifNotClientCredentialsToken('SecurityContext.getFamilyName', userInfo.familyName);
};
this.getEmail = function () {
return ifNotClientCredentialsToken('SecurityContext.getEmail', userInfo.email);
};
this.getUserName = function () {
if (grantType === constants.GRANTTYPE_CLIENTCREDENTIAL) {
return `client/${clientId}`;
} else {
return this.getUniquePrincipalName(origin, userInfo.logonName);
}
};
this.getUniquePrincipalName = function (origin, logonName) {
if (!ifNotClientCredentialsToken('SecurityContext.getUniquePrincipalName', true)) {
return null;
}
if (!origin) {
debugTrace('Origin claim not set in JWT. Cannot create unique user name. Returning null.');
return null;
}
if (!logonName) {
debugTrace('User login name claim not set in JWT. Cannot create unique user name. Returning null.');
return null;
}
if (origin.includes('/')) {
debugTrace('Illegal \'/\' character detected in origin claim of JWT. Cannot create unique user name. Retuning null.');
return null;
}
return `user/${origin}/${logonName}`;
};
this.getHdbToken = function () {
if (userAttributes && isForeignMode) {
debugTrace('\nThe SecurityContext has been initialized with an access token of a\n'
+ 'foreign OAuth Client Id and/or Identity Zone. Furthermore, the \n'
+ 'access token contains attributes. Due to the fact that we want to\n'
+ 'restrict attribute access to the application that provided the \n'
+ 'attributes, the getHdbToken function does not return a valid token.\n');
return null;
}
return samlToken ? samlToken : this.getAppToken();
};
this.getAppToken = function () {
return token;
};
this.getTokenInfo = function () {
return tokenInfo;
}
this.getAttributes = function () {
if (!ifNotClientCredentialsToken('SecurityContext.getAttribute', true)) {
return null;
}
if (!userAttributes) {
debugTrace('\nThe access token contains no user attributes.\n');
return null;
}
if (isForeignMode) {
debugTrace('\nThe SecurityContext has been initialized with an access token of a\n'
+ 'foreign OAuth Client Id and/or Identity Zone. Furthermore, the \n'
+ 'access token contains attributes. Due to the fact that we want to\n'
+ 'restrict attribute access to the application that provided the \n'
+ 'attributes, the getAttribute function does not return any attributes.\n');
return null;
}
return userAttributes;
}
this.getAttribute = function (name) {
const attributes = this.getAttributes();
if (!attributes) return null;
if (!name) {
debugTrace('\nInvalid attribute name (may not be null, empty, or undefined).');
return null;
}
if (!attributes[name]) {
debugTrace('\nNo attribute "' + name + '" found for user "' + this.getLogonName() + '".');
return null;
}
return attributes[name];
};
this.getAdditionalAuthAttribute = function (name) {
if (!additionalAuthAttributes) {
debugTrace('\nThe access token contains no additional authentication attributes.\n');
return null;
}
if (!name) {
debugTrace('\nInvalid attribute name (may not be null, empty, or undefined).');
return null;
}
if (!additionalAuthAttributes[name]) {
debugTrace('\nNo attribute "' + name + '" found as additional authentication attribute.');
return null;
}
return additionalAuthAttributes[name];
};
this.getCloneServiceInstanceId = function () {
return serviceinstanceid;
};
this.isInForeignMode = function () {
return isForeignMode;
};
this.hasAttributes = function () {
return ifNotClientCredentialsToken('SecurityContext.hasAttributes', userAttributes ? true : false);
};
this.checkLocalScope = function (scope) {
if (!scope || !scopes) {
return false;
}
var scopeName = xsappname + '.' + scope;
return scopes.indexOf(scopeName) !== -1;
};
this.getGrantType = function () {
return grantType;
};
this.checkScope = function (scope) {
if (!scope || !scopes) {
return false;
}
if (scope.substring(0, constants.XSAPPNAMEPREFIX.length) === constants.XSAPPNAMEPREFIX) {
scope = scope.replace(constants.XSAPPNAMEPREFIX, xsappname + '.');
}
return scopes.indexOf(scope) !== -1;
};
this.checkFollowingInstanceScope = function (scope) {
if (!scope || !scopes) {
return false;
}
const payload = this.getTokenInfo() ? this.getTokenInfo().getPayload() : null;
const cID = payload ? payload["client_id"] : "";
if(cID.indexOf('sb-') != 0) {
return false;
}
const appId = cID.substring(3);
if(!appId.indexOf("|") === -1) {
return false;
}
const scopeToSearch = appId + "." + scope;
return scopes.indexOf(scopeToSearch) !== -1;
}
function cleanUpUserAttributes(attr) {
for (var n in attr) {
return attr;
}
return null;
}
this.requestToken = function (serviceCredentials, type, additionalAttributes, cb) {
if (type === constants.TYPE_USER_TOKEN) {
return requests.requestUserToken(this.getAppToken(), serviceCredentials, additionalAttributes, null, this.getSubdomain(), cb);
} else if (type === constants.TYPE_CLIENT_CREDENTIALS_TOKEN) {
return requests.requestClientCredentialsToken(this.getSubdomain(), serviceCredentials, additionalAttributes, cb);
} else {
return cb(new Error('Invalid grant type.'));
}
};
function fillContext(encodedToken, info) {
tokenInfo = info;
var decodedToken = tokenInfo.getPayload();
debugTrace('\nApplication received a token of grant type "' + decodedToken.grant_type + '".');
token = encodedToken;
scopes = decodedToken.scope || [];
zid = tokenInfo.getAppTID();
subaccountid = decodedToken["ext_attr"] ? decodedToken["ext_attr"].subaccountid : zid;
if (!subaccountid) {
subaccountid = zid;
}
clientId = tokenInfo.getClientId();
expirationDate = new Date(decodedToken.exp * 1000);
grantType = decodedToken.grant_type;
origin = decodedToken.origin || null;
if (grantType !== constants.GRANTTYPE_CLIENTCREDENTIAL) {
var givenName, familyName;
if (decodedToken.ext_attr) {
givenName = decodedToken.ext_attr.given_name || null;
familyName = decodedToken.ext_attr.family_name || null;
}
userInfo.givenName = givenName || decodedToken.given_name || '';
userInfo.familyName = familyName || decodedToken.family_name || '';
userInfo.email = decodedToken.email || '';
userInfo.logonName = decodedToken.user_name || '';
debugTrace('\nObtained logon name: ' + this.getLogonName());
debugTrace('Obtained given name: ' + this.getGivenName());
debugTrace('Obtained family name: ' + this.getFamilyName());
debugTrace('Obtained email: ' + this.getEmail());
if (decodedToken.ext_cxt) {
userAttributes = decodedToken.ext_cxt['xs.user.attributes'] || null;
samlToken = decodedToken.ext_cxt['hdb.nameduser.saml'] || null;
} else {
userAttributes = decodedToken['xs.user.attributes'];
samlToken = decodedToken['hdb.nameduser.saml'] || null;
}
userAttributes = cleanUpUserAttributes(userAttributes);
if (userAttributes) {
debugTrace('\nObtained attributes: ' + JSON.stringify(userAttributes, null, 4));
} else {
debugTrace('\nObtained attributes: no XS user attributes in JWT token available.');
}
}
additionalAuthAttributes = decodedToken.az_attr || null;
if (additionalAuthAttributes) {
debugTrace('\nObtained additional authentication attributes: ' + JSON.stringify(additionalAuthAttributes, null, 4));
} else {
debugTrace('\nObtained attributes: no additional authentication attributes in JWT token available.');
}
if (decodedToken.ext_attr) {
serviceinstanceid = decodedToken.ext_attr.serviceinstanceid || null;
subdomain = decodedToken.ext_attr.zdn || null;
}
debugTrace('\nObtained subdomain: ' + this.getSubdomain());
debugTrace('Obtained serviceinstanceid: ' + this.getCloneServiceInstanceId());
debugTrace('Obtained origin: ' + this.getOrigin());
debugTrace('Obtained scopes: ' + JSON.stringify(scopes, null, 4));
}
this.verifyToken = function (encodedToken, attributes, cb) {
const validator = new JwtTokenValidatorXSUAA(configArr, config, attributes);
validator.validateToken(encodedToken, function (err, tokenInfo) {
if (err) {
try {
cb(err, null, tokenInfo);
} catch(e) {
debugError("xssec: Unhandled Exception in Callback");
debugError(e);
}
return;
}
isForeignMode = validator.isForeignMode();
//Token is now validated. So just fill local variables
fillContext.call(this, encodedToken, tokenInfo);
try {
cb(null, this, tokenInfo);
} catch(e) {
debugError("xssec: Unhandled Exception in Callback");
debugError(e);
}
}.bind(this));
};
};