@nuvo-prime/np-samlify
Version:
High-level API for Single Sign On (SAML 2.0)
364 lines • 13.4 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extract = exports.logoutResponseFields = exports.logoutRequestFields = exports.loginResponseFields = exports.logoutResponseStatusFields = exports.loginResponseStatusFields = exports.loginRequestFields = void 0;
var xpath_1 = require("xpath");
var utility_1 = require("./utility");
var api_1 = require("./api");
var camelcase_1 = __importDefault(require("camelcase"));
function buildAbsoluteXPath(paths) {
return paths.reduce(function (currentPath, name) {
var appendedPath = currentPath;
var isWildcard = name.startsWith('~');
if (isWildcard) {
var pathName = name.replace('~', '');
appendedPath = currentPath + "/*[contains(local-name(), '".concat(pathName, "')]");
}
if (!isWildcard) {
appendedPath = currentPath + "/*[local-name(.)='".concat(name, "']");
}
return appendedPath;
}, '');
}
function buildAttributeXPath(attributes) {
if (attributes.length === 0) {
return '/text()';
}
if (attributes.length === 1) {
return "/@".concat(attributes[0]);
}
var filters = attributes.map(function (attribute) { return "name()='".concat(attribute, "'"); }).join(' or ');
return "/@*[".concat(filters, "]");
}
exports.loginRequestFields = [
{
key: 'request',
localPath: ['AuthnRequest'],
attributes: ['ID', 'IssueInstant', 'Destination', 'AssertionConsumerServiceURL']
},
{
key: 'issuer',
localPath: ['AuthnRequest', 'Issuer'],
attributes: []
},
{
key: 'nameIDPolicy',
localPath: ['AuthnRequest', 'NameIDPolicy'],
attributes: ['Format', 'AllowCreate']
},
{
key: 'authnContextClassRef',
localPath: ['AuthnRequest', 'AuthnContextClassRef'],
attributes: []
},
{
key: 'signature',
localPath: ['AuthnRequest', 'Signature'],
attributes: [],
context: true
}
];
// support two-tiers status code
exports.loginResponseStatusFields = [
{
key: 'top',
localPath: ['Response', 'Status', 'StatusCode'],
attributes: ['Value'],
},
{
key: 'second',
localPath: ['Response', 'Status', 'StatusCode', 'StatusCode'],
attributes: ['Value'],
}
];
// support two-tiers status code
exports.logoutResponseStatusFields = [
{
key: 'top',
localPath: ['LogoutResponse', 'Status', 'StatusCode'],
attributes: ['Value']
},
{
key: 'second',
localPath: ['LogoutResponse', 'Status', 'StatusCode', 'StatusCode'],
attributes: ['Value'],
}
];
var loginResponseFields = function (assertion) { return [
{
key: 'conditions',
localPath: ['Assertion', 'Conditions'],
attributes: ['NotBefore', 'NotOnOrAfter'],
shortcut: assertion
},
{
key: 'response',
localPath: ['Response'],
attributes: ['ID', 'IssueInstant', 'Destination', 'InResponseTo'],
},
{
key: 'audience',
localPath: ['Assertion', 'Conditions', 'AudienceRestriction', 'Audience'],
attributes: [],
shortcut: assertion
},
// {
// key: 'issuer',
// localPath: ['Response', 'Issuer'],
// attributes: []
// },
{
key: 'issuer',
localPath: ['Assertion', 'Issuer'],
attributes: [],
shortcut: assertion
},
{
key: 'nameID',
localPath: ['Assertion', 'Subject', 'NameID'],
attributes: [],
shortcut: assertion
},
{
key: 'sessionIndex',
localPath: ['Assertion', 'AuthnStatement'],
attributes: ['AuthnInstant', 'SessionNotOnOrAfter', 'SessionIndex'],
shortcut: assertion
},
{
key: 'attributes',
localPath: ['Assertion', 'AttributeStatement', 'Attribute'],
index: ['Name'],
attributePath: ['AttributeValue'],
attributes: [],
shortcut: assertion
}
]; };
exports.loginResponseFields = loginResponseFields;
exports.logoutRequestFields = [
{
key: 'request',
localPath: ['LogoutRequest'],
attributes: ['ID', 'IssueInstant', 'Destination']
},
{
key: 'issuer',
localPath: ['LogoutRequest', 'Issuer'],
attributes: []
},
{
key: 'nameID',
localPath: ['LogoutRequest', 'NameID'],
attributes: []
},
{
key: 'sessionIndex',
localPath: ['LogoutRequest', 'SessionIndex'],
attributes: []
},
{
key: 'signature',
localPath: ['LogoutRequest', 'Signature'],
attributes: [],
context: true
}
];
exports.logoutResponseFields = [
{
key: 'response',
localPath: ['LogoutResponse'],
attributes: ['ID', 'Destination', 'InResponseTo']
},
{
key: 'issuer',
localPath: ['LogoutResponse', 'Issuer'],
attributes: []
},
{
key: 'signature',
localPath: ['LogoutResponse', 'Signature'],
attributes: [],
context: true
}
];
function extract(context, fields) {
var dom = (0, api_1.getContext)().dom;
var rootDoc = dom.parseFromString(context);
return fields.reduce(function (result, field) {
var _a, _b, _c, _d, _e, _f;
// get essential fields
var key = field.key;
var localPath = field.localPath;
var attributes = field.attributes;
var isEntire = field.context;
var shortcut = field.shortcut;
// get optional fields
var index = field.index;
var attributePath = field.attributePath;
// set allowing overriding if there is a shortcut injected
var targetDoc = rootDoc;
// if shortcut is used, then replace the doc
// it's a design for overriding the doc used during runtime
if (shortcut) {
targetDoc = dom.parseFromString(shortcut);
}
// special case: multiple path
/*
{
key: 'issuer',
localPath: [
['Response', 'Issuer'],
['Response', 'Assertion', 'Issuer']
],
attributes: []
}
*/
if (localPath.every(function (path) { return Array.isArray(path); })) {
var multiXPaths = localPath
.map(function (path) {
// not support attribute yet, so ignore it
return "".concat(buildAbsoluteXPath(path), "/text()");
})
.join(' | ');
return __assign(__assign({}, result), (_a = {}, _a[key] = (0, utility_1.uniq)((0, xpath_1.select)(multiXPaths, targetDoc).map(function (n) { return n.nodeValue; }).filter(utility_1.notEmpty)), _a));
}
// eo special case: multiple path
var baseXPath = buildAbsoluteXPath(localPath);
var attributeXPath = buildAttributeXPath(attributes);
// special case: get attributes where some are in child, some are in parent
/*
{
key: 'attributes',
localPath: ['Response', 'Assertion', 'AttributeStatement', 'Attribute'],
index: ['Name'],
attributePath: ['AttributeValue'],
attributes: []
}
*/
if (index && attributePath) {
// find the index in localpath
var indexPath = buildAttributeXPath(index);
var fullLocalXPath = "".concat(baseXPath).concat(indexPath);
var parentNodes = (0, xpath_1.select)(baseXPath, targetDoc);
// [uid, mail, edupersonaffiliation], ready for aggregate
var parentAttributes = (0, xpath_1.select)(fullLocalXPath, targetDoc).map(function (n) { return n.value; });
// [attribute, attributevalue]
var childXPath = buildAbsoluteXPath([(0, utility_1.last)(localPath)].concat(attributePath));
var childAttributeXPath = buildAttributeXPath(attributes);
var fullChildXPath_1 = "".concat(childXPath).concat(childAttributeXPath);
// [ 'test', 'test@example.com', [ 'users', 'examplerole1' ] ]
var childAttributes = parentNodes.map(function (node) {
var nodeDoc = dom.parseFromString(node.toString());
if (attributes.length === 0) {
var childValues = (0, xpath_1.select)(fullChildXPath_1, nodeDoc).map(function (n) { return n.nodeValue; });
if (childValues.length === 1) {
return childValues[0];
}
return childValues;
}
if (attributes.length > 0) {
var childValues = (0, xpath_1.select)(fullChildXPath_1, nodeDoc).map(function (n) { return n.value; });
if (childValues.length === 1) {
return childValues[0];
}
return childValues;
}
return null;
});
// aggregation
var obj = (0, utility_1.zipObject)(parentAttributes, childAttributes, false);
return __assign(__assign({}, result), (_b = {}, _b[key] = obj, _b));
}
// case: fetch entire content, only allow one existence
/*
{
key: 'signature',
localPath: ['AuthnRequest', 'Signature'],
attributes: [],
context: true
}
*/
if (isEntire) {
var node = (0, xpath_1.select)(baseXPath, targetDoc);
var value = null;
if (node.length === 1) {
value = node[0].toString();
}
if (node.length > 1) {
value = node.map(function (n) { return n.toString(); });
}
return __assign(__assign({}, result), (_c = {}, _c[key] = value, _c));
}
// case: multiple attribute
/*
{
key: 'nameIDPolicy',
localPath: ['AuthnRequest', 'NameIDPolicy'],
attributes: ['Format', 'AllowCreate']
}
*/
if (attributes.length > 1) {
var baseNode = (0, xpath_1.select)(baseXPath, targetDoc).map(function (n) { return n.toString(); });
var childXPath_1 = "".concat(buildAbsoluteXPath([(0, utility_1.last)(localPath)])).concat(attributeXPath);
var attributeValues = baseNode.map(function (node) {
var nodeDoc = dom.parseFromString(node);
var values = (0, xpath_1.select)(childXPath_1, nodeDoc).reduce(function (r, n) {
r[(0, camelcase_1.default)(n.name, { locale: 'en-us' })] = n.value;
return r;
}, {});
return values;
});
return __assign(__assign({}, result), (_d = {}, _d[key] = attributeValues.length === 1 ? attributeValues[0] : attributeValues, _d));
}
// case: single attribute
/*
{
key: 'statusCode',
localPath: ['Response', 'Status', 'StatusCode'],
attributes: ['Value'],
}
*/
if (attributes.length === 1) {
var fullPath = "".concat(baseXPath).concat(attributeXPath);
var attributeValues = (0, xpath_1.select)(fullPath, targetDoc).map(function (n) { return n.value; });
return __assign(__assign({}, result), (_e = {}, _e[key] = attributeValues[0], _e));
}
// case: zero attribute
/*
{
key: 'issuer',
localPath: ['AuthnRequest', 'Issuer'],
attributes: []
}
*/
if (attributes.length === 0) {
var attributeValue = null;
var node = (0, xpath_1.select)(baseXPath, targetDoc);
if (node.length === 1) {
var fullPath = "string(".concat(baseXPath).concat(attributeXPath, ")");
attributeValue = (0, xpath_1.select)(fullPath, targetDoc);
}
if (node.length > 1) {
attributeValue = node.filter(function (n) { return n.firstChild; })
.map(function (n) { return n.firstChild.nodeValue; });
}
return __assign(__assign({}, result), (_f = {}, _f[key] = attributeValue, _f));
}
return result;
}, {});
}
exports.extract = extract;
//# sourceMappingURL=extractor.js.map
;