UNPKG

@attivio/suit

Version:

Attivio SUIT, the Search UI Toolkit, is a library for creating search clients for searching the Attivio platform.

541 lines (470 loc) 19.1 kB
'use strict'; exports.__esModule = true; exports.default = undefined; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _class, _temp; var _md = require('crypto-js/md5'); var _md2 = _interopRequireDefault(_md); var _FetchUtils = require('./FetchUtils'); var _FetchUtils2 = _interopRequireDefault(_FetchUtils); var _ObjectUtils = require('./ObjectUtils'); var _ObjectUtils2 = _interopRequireDefault(_ObjectUtils); var _StringUtils = require('./StringUtils'); var _StringUtils2 = _interopRequireDefault(_StringUtils); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Utility class for handling auhentication in SUIT-based applications. Also handles * configuration of application including the configuration used for components * wrapped with the Configurable higher-order component. */ var AuthUtils = (_temp = _class = function () { function AuthUtils() { _classCallCheck(this, AuthUtils); } /** * Called by the application to pass in configuration to the * library's utility and API classes. * * @param users the contents of the users.xml file, converted to JavaScript objects * @param config the contents of the configuration.properties.js file with any * maps converted already * @param simpleValidation if set to true, then a lot of the validation * specific to search applications won't be done * @returns a configuraiton error message, if one occurred */ AuthUtils.configure = function configure(users, config) { var simpleValidation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var mappifiedConfig = AuthUtils.mappify(config); var configError = AuthUtils.validateConfiguration(mappifiedConfig, simpleValidation); if (configError) { return configError; } if (mappifiedConfig.ALL.authType === 'XML') { // Only validate users if the auth type is XML var usersError = AuthUtils.validateUsers(users); if (usersError) { return usersError; } } AuthUtils.users = users; AuthUtils.config = mappifiedConfig; return null; }; /** Go through the passed in object and conver any nested objects to JavaScript * Map objects if and only if their name starts with a lowercase letter. Thus * orig.ALL and orig.Masthead won't be converted but orig.ALL.entityColors * will. */ /** * The special role representing a top-level admin, granting full permission. */ AuthUtils.mappify = function mappify(orig) { var copy = {}; Object.entries(orig).forEach(function (entry) { var key = entry[0], value = entry[1]; if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value !== null && (!Array.isArray(value) || value instanceof Map)) { // If the value is an object that's not null or an Array or a Map, convert it if (key.charAt(0) === key.charAt(0).toLowerCase()) { copy[key] = _ObjectUtils2.default.toMap(value); } else { // recurse on non-final objects copy[key] = AuthUtils.mappify(value); } } else { // Just set it as is copy[key] = value; } }); return copy; }; /** * Logs the currently logged-in user out. */ AuthUtils.logout = function logout() { // Remove the user we added to session storage sessionStorage.removeItem(AuthUtils.USER_KEY); // If it's just XML authentication, removing saved user (above) is good enough. // Otherwise, we need to also tell the server if (AuthUtils.config.authType !== 'XML') { // The servlet mimics the node's login endpoint with the logout action to log out of the SAML IdP // so regardless of how we're authenticated, we just need to access that to log out. // But first, remove the session cookies var baseUri = AuthUtils.getConfig().ALL.baseUri; document.cookie = 'SessionId=; Path=' + baseUri + '; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; document.cookie = 'JSESSIONID=; Path=' + baseUri + '; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; var headers = new Headers({ Accept: 'application/json', 'Content-Type': 'application/json' }); var params = { method: 'GET', headers: headers, credentials: 'include', mode: 'cors' }; var fetchRequest = new Request('login?action=logout', params); fetch(fetchRequest).then(function () /* response: Response */{ var loggedOutUri = baseUri + '?action=loggedout'; // let loggedOutUri = baseUri; // if (AuthUtils.getConfig() && AuthUtils.getConfig().ALL && AuthUtils.getConfig().ALL.authType === 'XML') { // // If we're doing XML authentication, go to the login page with the "We've logged you out" flag set // } setTimeout(function () { document.location.assign(loggedOutUri); }, 100); }).catch(function (error) { console.warn('Failed to log out:', error); }); } }; /** * Implementation of the obfuscation algorithm from Jetty. */ AuthUtils.obfuscate = function obfuscate(orig) { var buf = []; var bytes = orig.split('').map(function (c) { return c.charCodeAt(0); }); bytes.forEach(function (b1, i) { var b2 = bytes[orig.length - (i + 1)]; var i1 = 127 + b1 + b2; var i2 = 127 + b1 - b2; var i0 = i1 * 256 + i2; var newChar = i0.toString(36); switch (newChar.length) { case 1: buf.push('000' + newChar); break; case 2: buf.push('00' + newChar); break; case 3: buf.push('0' + newChar); break; default: buf.push(newChar); break; } }); return buf.join(''); }; /** * Validate a password against a hashed one. */ AuthUtils.passwordMatches = function passwordMatches(comp, compTo) { if (compTo.startsWith('OBF:')) { var remainder = compTo.substring(4); var obfuscatedComp = AuthUtils.obfuscate(comp); return obfuscatedComp === remainder; } else if (compTo.startsWith('MD5:')) { var _remainder = compTo.substring(4); var md5Comp = (0, _md2.default)(comp).toString(); return md5Comp === _remainder; } return comp === compTo; }; /** * Find the user info for a given user name. * This is only applicable in the case of XML authentication. */ AuthUtils.findUser = function findUser(username) { if (this.config.ALL.authType === 'XML') { if (Array.isArray(AuthUtils.users.principals.user)) { return AuthUtils.users.principals.user.find(function (testUser) { return testUser.$.id === username; }); } // This will happen if there's only use user... if (AuthUtils.users.principals.user.$.id === username) { return AuthUtils.users.principals.user; } } return null; }; /** * Attempt to log the specified user in. If an error * occurs logging in, then it is returned. Otherwise * this returns null. The log in is valid for the timeout * set in the authentication configuration. */ AuthUtils.login = function login(username, password) { if (!AuthUtils.config || !AuthUtils.config.ALL) { return null; } if (AuthUtils.config.ALL.authType === 'NONE') { return null; } if (AuthUtils.config.ALL.authType === 'XML') { var userObject = AuthUtils.findUser(username); if (userObject) { if (AuthUtils.passwordMatches(password, userObject.$.password)) { sessionStorage.setItem(AuthUtils.USER_KEY, JSON.stringify(userObject)); return null; } } return new Error('Invalid log-in credentials.'); } return null; }; /** * Save the currently logged-in user's info into session storage for easy access. */ AuthUtils.saveLoggedInUser = function saveLoggedInUser(userInfo) { if (userInfo) { var userInfoCopy = JSON.parse(JSON.stringify(userInfo)); sessionStorage.setItem(AuthUtils.USER_KEY, JSON.stringify(userInfoCopy)); } else { sessionStorage.removeItem(AuthUtils.USER_KEY); } }; /** * Check whether the user has a particular permission. */ AuthUtils.hasRole = function hasRole(user, role) { if (AuthUtils.config && AuthUtils.config.ALL && AuthUtils.config.ALL.authType === 'NONE') { // check if the user has inherited or been directly assigned the provided role if (user.roles && (user.roles.includes(role) || // The admin role is equivalent to all roles user.roles.includes(AuthUtils.ADMIN_ROLE))) { return true; } } return false; }; /** * Check whether there is a user currently logged in. * Optionally with the provided role */ AuthUtils.isLoggedIn = function isLoggedIn(role) { if (!AuthUtils.config || !AuthUtils.config.ALL) { return false; } var user = AuthUtils.getSavedUser(); if (user) { if (role) { return AuthUtils.hasRole(user, role); } return true; } return false; }; /** * Get the full info object for a logged-in user we already know about. * If no user is logged in or if the logged-in user's login has expired, * then this method returns null. */ AuthUtils.getSavedUser = function getSavedUser() { var userObjectJson = sessionStorage.getItem(AuthUtils.USER_KEY); if (userObjectJson) { var userObject = JSON.parse(userObjectJson); return userObject; } return null; }; /** * Get the full info object for the logged-in user and call the passed-in * callback function with this info. If no user is logged in, the callback * is passed null. * * @param callback a function which takes the user info as a parameter */ AuthUtils.getLoggedInUserInfo = function getLoggedInUserInfo(callback) { var userObject = AuthUtils.getSavedUser(); if (userObject) { callback(userObject); } else if (AuthUtils.config.ALL.authType === 'SAML' || AuthUtils.config.ALL.authType === 'NONE') { // If the authentication is done on the front-end, we shouldn't // ever get here because if there's no saved user, then // no one is logged in yet... var fetchResponseCallback = function fetchResponseCallback(userInfo, error) { if (userInfo) { AuthUtils.saveLoggedInUser(userInfo); } if (error) { console.error('Got an error retrieving the current user\'s details.', error); } callback(userInfo); }; _FetchUtils2.default.fetch(AuthUtils.config.ALL.baseUri + '/rest/serverDetailsApi/user', null, fetchResponseCallback, 'GET', 'Got an error retrieving the current user\u2019s details.'); } else { // If we're doing our own authentication, and nobody is logged in, pass null to the callback callback(null); } }; AuthUtils.getLoggedInUserId = function getLoggedInUserId() { var userInfo = AuthUtils.getSavedUser(); if (userInfo) { if (userInfo.$ && userInfo.$.id) { // Special case for XML-based user authentication return userInfo.$.id; } if (userInfo.userId) { return userInfo.userId; } } return ''; }; /** * Get the user name to display given the user info passed in. */ AuthUtils.getUserName = function getUserName(userInfo) { if (userInfo) { if (userInfo.$) { // Special case for XML-based user authentication if (userInfo.$.name && userInfo.$.name.length > 0) { return userInfo.$.name; } return userInfo.$.id; } if (userInfo.fullName) { return userInfo.fullName; } if (userInfo.firstName && userInfo.lastName) { return userInfo.firstName + ' ' + userInfo.lastName; } if (userInfo.firstName) { return userInfo.firstName; } if (userInfo.lastName) { return userInfo.lastName; } if (userInfo.userId) { return userInfo.userId; } } return ''; }; /** * Validate the users object to make sure it won't cause us any * grief. Return null if it's good, or an error message otherwise. * This should only be called if auth type is 'XML' */ AuthUtils.validateUsers = function validateUsers(users) { if (!users) { return 'The users.xml file was not properly loaded.'; } if (!users.principals) { return 'The users.xml file is invalid; it must contain an outer <principals> element.'; } if (!users.principals.user) { return 'The users.xml file is invalid; it must contain at least one <user> definition.'; } if (Array.isArray(users.principals.user)) { for (var i = 0; i < users.principals.user.length; i += 1) { if (!users.principals.user[i].$ || !_StringUtils2.default.notEmpty(users.principals.user[i].$.id)) { return 'The users.xml file is invalid; the user at position ' + i + ' is missing an "id" attribute.'; } } } else if (!users.principals.user.$ || !_StringUtils2.default.notEmpty(users.principals.user.$.id)) { return 'The users.xml file is invalid; the user is missing an "id" attribute.'; } return null; }; /** * Validate the configuration object to make sure it won't cause us any * grief. Return null if it's good, or an error message otherwise. */ AuthUtils.validateConfiguration = function validateConfiguration(config) { var simpleValidation = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (!config) { return 'The configuration object must be specified.'; } if (!config.ALL) { return 'The configuration object is missing the \'ALL\' value.'; } if (config.ALL.baseUri === null || typeof config.ALL.baseUri === 'undefined') { // Note that baseUri can be the empty string, as in the case of running inside a node return 'The configuration object is missing the \'ALL.baseUri\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.basename)) { return 'The configuration object is missing the \'ALL.basename\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.authType)) { return 'The configuration object is missing the \'ALL.authType\' value.'; } if (config.ALL.authType !== 'XML' && config.ALL.authType !== 'SAML' && config.ALL.authType !== 'NONE') { return 'The configuration object has an invalid value for \'ALL.authType\': it must be \'XML,\' \'SAML,\' or \'NONE\' but it is \'' + config.ALL.authType + '.\''; // eslint-disable-line max-len } if (config.ALL.authType === 'XML' && !config.ALL.loginPage) { return 'The configuration is missing the \'All.loginPage\' value whiich is required when authentication is set to XML.'; } if (!_StringUtils2.default.notEmpty(config.ALL.defaultRealm)) { return 'The configuration object is missing the \'ALL.defaultRealm\' value.'; } if (!simpleValidation) { if (!config.ALL.entityFields) { return 'The configuration object is missing the \'ALL.entityFields\' value.'; } if (!(config.ALL.entityFields instanceof Map)) { return 'The configuration object\'s \'ALL.entityFields\' value should be a Map.'; } if (!config.ALL.entityColors) { return 'The configuration object is missing the \'ALL.entityColors\' value.'; } if (!(config.ALL.entityColors instanceof Map)) { return 'The configuration object\'s \'ALL.entityColors\' value should be a Map.'; } if (!config.ALL.fields) { return 'The configuration object is missing the \'ALL.fields\' value.'; } if (!Array.isArray(config.ALL.fields)) { return 'The configuration object\'s \'ALL.fields\' value should be an array with at least one value.'; } if (config.ALL.fields.length < 1) { return 'The configuration object\'s \'ALL.fields\' value should be an array with at least one value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.title)) { return 'The configuration object is missing the \'ALL.title\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.uri)) { return 'The configuration object is missing the \'ALL.uri\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.table)) { return 'The configuration object is missing the \'ALL.table\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.latitude)) { return 'The configuration object is missing the \'ALL.latitude\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.longitude)) { return 'The configuration object is missing the \'ALL.longitude\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.mimetype)) { return 'The configuration object is missing the \'ALL.mimetype\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.sourcePath)) { return 'The configuration object is missing the \'ALL.sourcePath\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.previewImageUri)) { return 'The configuration object is missing the \'ALL.previewImageUri\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.thumbnailImageUri)) { return 'The configuration object is missing the \'ALL.thumbnailImageUri\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.moreLikeThisQuery)) { return 'The configuration object is missing the \'ALL.moreLikeThisQuery\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.teaser)) { return 'The configuration object is missing the \'ALL.teaser\' value.'; } if (!_StringUtils2.default.notEmpty(config.ALL.text)) { return 'The configuration object is missing the \'ALL.text\' value.'; } } return null; }; AuthUtils.getEntityColors = function getEntityColors() { if (this.config && this.config.ALL && this.config.ALL.entityColors) { return this.config.ALL.entityColors; } // If it's not configured, return an empty map. return new Map(); }; AuthUtils.getConfig = function getConfig() { return this.config; }; return AuthUtils; }(), _class.USER_KEY = 'suit-user', _class.ADMIN_ROLE = 'AIE_Administrator', _temp); exports.default = AuthUtils; module.exports = exports['default'];