UNPKG

@testcomplete/usermanager

Version:

A library that pick a credentials group session from a catalogue for a testing execution

671 lines (575 loc) 24.1 kB
// Check for NodeJS. If NodeJS, require need relative path let sPrePath = typeof process !== 'undefined' ? '@testcomplete/' : ''; let sPrePathRel = typeof process !== 'undefined' ? './' : ''; let sjcl = require(`sjcl`); let base64 = require(`${sPrePathRel}Base64`); let fs = require(`${sPrePath}FileSystemUtil`); let logger = require(`${sPrePath}LoggerUtil`); let lockManager = require(`${sPrePath}LockManager`); /** * Version v0.3.2 * * @author: Nicolas DUPRE (VISEO) * * Interface to handle Users for connexions using a Catalogue. * It allows concurrent run execution without using the same User at once. * * @return {UserManager} Constructor * * @constructor */ function UserManager() { let self = this; /** * @type {string} SJCL Crypting Passphrase. * @private */ self._sPassphrase = 'TestComplete'; /** * @type {String} Path to the folder which contains lock files. * @private */ self._sLockFolderPath = null; /** * @type {Object} Path to the JSON File containing users. * @private */ self._sUserCataloguePath = null; /** * @type {Object} Retrieve users for locked sessions. * @private */ self._oUsers = null; /** * Initial base for catalogue to be function. Use by initializeCatalogue. * * @type {{common: {}, passwords: {}, group_sessions: {}}} * @private */ self._oCatalogueBase = { "common": {}, "group_sessions": {}, "passwords": {} }; /** * @type {Object} User catalogue. * @private */ self._oCatalogue = null; /** * Lock File Util Manager (no time aspect required here) */ self._lockManager = new lockManager().setTimeout(0).setInterval(0); /** * Define the path to the folder containing lock files. *Zth without ending /. * * @return {UserManager} Instance */ self.setLockFolderLocation = function ($sLockFolderLocation) { // Memorize folder path self._sLockFolderPath = $sLockFolderLocation; // Setting Up Lock Manager Folder Path self._lockManager.setLockFolderPath(self._sLockFolderPath); return self; }; /** * Define the path to the JSON file containing users. * * @param {String} $sUserCatalogueLocation Path to the user catalogue file. * * @return {UserManager} Instance */ self.setUserCatalogueLocation = function ($sUserCatalogueLocation) { self._sUserCataloguePath = $sUserCatalogueLocation; return self; }; self.catalogue = function () { if (!self._oCatalogue) { self.getUserCatalogue(); } let oCatalogue = self._oCatalogue; /** * [X] session( ).add(x) * [X] session(x).delete() * [ ] session(x).rename(y) * * [ ] session(x).credential(y).user(z).set([p]) * [ ] session(x).credential(y).user(z).password(p) * * [X] credential(x).add() * [X] credential(x).delete() * [ ] credential(x).rename(y) * * [X] credential(x).user(y).setPassword(z) * * */ return { session: function ($sSession = '') { let oSession = null; if ($sSession) { if (!oCatalogue.group_sessions[$sSession]) { oSession = self.catalogue().session().add($sSession); } else { oSession = oCatalogue.group_sessions[$sSession]; } } return { /** * Add new session group. * * @param {String} $sSession New Session group name. */ add: function ($sSession) { let oNewSession = {}; for (let sCredential in self._oCatalogue.common) { oNewSession[sCredential] = '<setUser>'; } return self._oCatalogue.group_sessions[$sSession] = oNewSession; }, /** * Remove session group from catalogue. */ delete: function () { if (oSession) { delete self._oCatalogue.group_sessions[$sSession]; } return self; }, rename: function ($sNewSession) { //[...x] }, credential: function ($sCredential) { //let oCredential = oSession[$sCredential]; if ($sCredential) { if (oSession[$sCredential]) { //oCredential = oSession[$sCredential]; } else { self.logger().error(`Credential System '${$sCredential}' undefined in '${self._sUserCataloguePath}'`); } } else { self.logger().error(`Please specified which Credential System to manage.`); } return { user: function ($sUser) { return { set: function ($sPassword = null) { if ($sUser) { oSession[$sCredential] = $sUser; } if ($sPassword !== null) { self.catalogue().credential($sCredential).user($sUser).setPassword($sPassword); } }, password: function ($sPassword = null) { self.catalogue().credential($sCredential).user($sUser).setPassword($sPassword); } } } } } }; }, credential: function ($sCredential = '') { return { /** * Register a new credential system in the user catalogue. * * @param {String} $oSetting Common settings for the new cred system. * @param {String} $sUser Set default user for new cred system. * @param {String} $sPassword Password for default user. */ add: function ($oSetting = {}, $sUser = '<setUser>', $sPassword = '') { // Adding / Updating common settings if (!self._oCatalogue.common[$sCredential]) { self._oCatalogue.common[$sCredential] = {}; } self._oCatalogue.common[$sCredential] = Object.assign(self._oCatalogue.common[$sCredential], $oSetting); // Update Session Group to add credential systems for (let oSession in self._oCatalogue.group_sessions) { self._oCatalogue.group_sessions[oSession][$sCredential] = $sUser; } // Update / Set default user password if (!self._oCatalogue.passwords[$sCredential]) { self._oCatalogue.passwords[$sCredential] = {}; } self.catalogue().credential($sCredential).user($sUser).setPassword($sPassword); }, /** * Delete credential system from user catalogue. */ delete: function () { // Delete cred system for common settings if (self._oCatalogue.common[$sCredential]) { delete self._oCatalogue.common[$sCredential]; } // Delete cred system for each session group for (let sSession in self._oCatalogue.group_sessions) { if (self._oCatalogue.group_sessions[sSession][$sCredential]) { delete self._oCatalogue.group_sessions[sSession][$sCredential]; } } // Delete passwords if (self._oCatalogue.passwords[$sCredential]) { delete self._oCatalogue.passwords[$sCredential]; } }, rename: function () { // }, /** * User manager at credential system level * * @param {String} $sUser Username to manage for the credential system. * * @return {{setPassword: setPassword}} */ user: function ($sUser = '') { return { /** * Set encrypted password for specified user under specified credential system * @param {String} $sPassword Password for the user */ setPassword: function ($sPassword = '') { if ($sCredential && $sUser) { if (!self._oCatalogue.passwords[$sCredential]) { self._oCatalogue.passwords[$sCredential] = {} } self._oCatalogue.passwords[$sCredential][$sUser] = base64.encode( sjcl.encrypt(self._sPassphrase, $sPassword) ); } } } } } }, /** * Save user catalogue. */ save: function () { self.filePutContent(self._sUserCataloguePath, JSON.stringify(self._oCatalogue)); } }; }; /** * File System for NodeJS & TestComplete compatibility. * * @return {{read: (function(*=): *), exists: exists, write: write}|{read: (function(*=): *), exists: (function(*=): *), write: (function(*=, *=): *)}} */ self.fs = fs; /** * Logger System for NodeJS & TestComplete compatibility. * * @return {{warning: *, message: *, error: *}} */ self.logger = logger; /** * Indicates if the UserManager interface has encounter an error (path issue). * * @return {boolean} */ self.hasError = function () { return self._oUsers.runError; }; /** * Get the content of the specified file. * * @param {String} $sFileLocation Path of the file to retrieve it content. * * @return {String} File content */ self.fileGetContent = function ($sFileLocation) { return self.fs().read($sFileLocation); }; /** * Clear and insert the provided string into specified file. * * @param {String} $sFileLocation Path to the file to write. * @param {String} $sContent Content to insert in the file. */ self.filePutContent = function ($sFileLocation, $sContent) { return self.fs().write($sFileLocation, $sContent); }; /** * Get (& set if not exist) TestComplete ProjectSuite var containing run session users. * * @return {Object} */ self.getStorageVar = function () { // Check if PJS Variable Exists if (!ProjectSuite.Variables.VariableExists('sUsers')) { ProjectSuite.Variables.AddVariable('sUsers', 'String'); ProjectSuite.Variables.sUsers = JSON.stringify({ runId: null, runError: false }); } return self._oUsers = JSON.parse(ProjectSuite.Variables.sUsers); }; /** * Generic definition to get user, password and extra setting for a system. * * @param {String} $sSystem Credential system to handle. */ self.systemUser = function ($sSystem) { let oObject = {}; oObject = Object.assign(oObject, self._oUsers[$sSystem]); oObject = Object.assign(oObject, { /** * Return the credential system user id. * * @return {string} */ user: function () { return self._oUsers[$sSystem].user; }, /** * Return the decrypt password of the credential system. * * @return {String} */ password: function () { return sjcl.decrypt(self._sPassphrase, base64.decode(self._oUsers[$sSystem].password)); }, /** * Handle a flag indicating if the user is currently signed or not * * @return {{off: off, on: on}} */ signed: function () { return { /** * Indicates the user is currently signed */ on: function () { self._oUsers[$sSystem].signed = true; // Store new status self.saveStorageVar(); }, /** * Indicates the user is currently off */ off: function () { self._oUsers[$sSystem].signed = false; // Store new status self.saveStorageVar(); } } }, /** * Return the signed status of the user. * * @return {oObject.signed|(function(): {off: off, on: on})|boolean} */ isSigned: function () { return self._oUsers[$sSystem].signed; } }); return oObject; }; /** * Retrieve or get a new session users group for the run. * * @param {String} $sSessionGroup Session group from user catalogue. * * @return {UserManager} */ self.getUsers = function ($sSessionGroup) { //let sSessionGroupLockFile = null; let sSessionGroup = null; // Load User Catalogue let oCatalogue = self.getUserCatalogue(); // Get for a valid sessions group if ($sSessionGroup) { //@TODO: [X] use LockManager.lockExists() // Check if Session Group is locked // sSessionGroupLockFile = `${self._sLockFolderPath}/${$sSessionGroup}`; // // if (self.fs().exists(sSessionGroupLockFile)) { // sSessionGroup = $sSessionGroup; // } if (self._lockManager.isLockExists($sSessionGroup)) { sSessionGroup = $sSessionGroup; } } // If provided session is no longer locked, // Check for another one if (!sSessionGroup) { // Check for an available session group for (let sSession in oCatalogue.group_sessions) { if(!oCatalogue.group_sessions.hasOwnProperty(sSession)) continue; //@TODO: [X] use LockManager.lockExists() // sSessionGroupLockFile = `${self._sLockFolderPath}/${sSession}`; // // if (!self.fs().exists(sSessionGroupLockFile)) { // sSessionGroup = sSession; // break; // } if (!self._lockManager.isLockExists(sSession)) { sSessionGroup = sSession; break; } } } // Get Users & Lock Sessions if (sSessionGroup) { // @TODO: remove deprected next to test validation // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // let sRunIdLockFile = `${self._sLockFolderPath}/${self._oUsers.runId}`; // sSessionGroupLockFile = `${self._sLockFolderPath}/${sSessionGroup}`; // self.fs().write(sRunIdLockFile, sSessionGroup); // self.fs().write(sSessionGroupLockFile); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< self._lockManager.setLockName(self._oUsers.runId).lock(null, sSessionGroup); self._lockManager.setLockName(sSessionGroup).lock(null, self._oUsers.runId); self.logger().message(`Current execution (Lock '${self._oUsers.runId}') use session group '${sSessionGroup}'.`); let oCommons = self._oCatalogue.common; let oPasswords = self._oCatalogue.passwords; let oSessionUsers = self._oCatalogue.group_sessions[sSessionGroup]; for (let system in oSessionUsers) { if(!oSessionUsers.hasOwnProperty(system)) continue; let oSystemCommon = oCommons[system] || {}; let sUser = oSessionUsers[system] || ''; let sPassword = oPasswords[system][sUser] || ''; let bSigned = false; if (self._oUsers[system] && self._oUsers[system].hasOwnProperty('signed')) { bSigned = self._oUsers[system].signed } self._oUsers[system] = Object.assign(oSystemCommon, { user: sUser, password: sPassword, signed: bSigned }); self[system] = self.systemUser.bind(self, system); } } else { // Check if Error already encounter to prevent loop. if (!self._oUsers.runError) { // Set Run as error encountered self._oUsers.runError = true; // Store new status self.saveStorageVar(); // Log Error (Trigger Event) self.logger().error(`Not enough Session Group available for the run. Check folder '${self._sLockFolderPath}' or add more Sessions in '${self._sUserCataloguePath}'`); } } return self; }; /** * Load User catalogue. * * @return {Object} Stored User Catalogue. */ self.getUserCatalogue = function () { if (!self._sUserCataloguePath) { self.logger().error(`Path to the user catalogue is not define. Please use UserManager.setUserCatalogueLocation('/Path/to/catalogue.json').`); } if (!self.fs().exists(self._sUserCataloguePath)) { self.logger().error(`Path to the user catalogue '${self._sUserCataloguePath}' does not exists`); } try { return self._oCatalogue = JSON.parse(self.fileGetContent(self._sUserCataloguePath)); } catch ($sErr) { self.logger.error(`Can not parse JSON file '${self._sUserCataloguePath}' : ${$sErr}`); } }; /** * Initialize the base JSON file of the user catalogue. * * @return {boolean} */ self.initializeCatalogue = function () { if (!self._sUserCataloguePath) { self.logger().error(`Path to the user catalogue is not define. Please use UserManager.setUserCatalogueLocation('/Path/to/catalogue.json').`); return false; } if (self.fs().exists(self._sUserCataloguePath)) { self.logger().error(`The user catalogue '${self._sUserCataloguePath}' already exists`); return false; } else { self.fs().write(self._sUserCataloguePath, JSON.stringify(self._oCatalogueBase)); return true; } }; /** * Save locked group session users. * * @return {boolean} */ self.saveStorageVar = function () { ProjectSuite.Variables.sUsers = JSON.stringify(self._oUsers); return true; }; /** * Load, retrieve/get a group session user. * * @return {UserManager} */ self.getUserSessionGroup = function () { // Get Data let oUsers = self.getStorageVar(); // Get & Check runId if (!oUsers.runId) { // Generates ID oUsers.runId = new Date().getTime(); } // Check if there is a socket for this runId // @TODO: [X] LockManager lockExists // let sRunIdSocketPath = `${self._sLockFolderPath}/${oUsers.runId}`; // let sSessionGroup = null; //if (aqFile.Exists(sRunIdSocketPath)) { // if (self.fs().exists(sRunIdSocketPath)) { // // Read file to find assigned Session Group // sSessionGroup = self.fileGetContent(sRunIdSocketPath); // } let sSessionGroup = null; if (self._lockManager.isLockExists(oUsers.runId)) { sSessionGroup = self._lockManager.getLockContent(oUsers.runId); } // Load Users self.getUsers(sSessionGroup); // Save Data self.saveStorageVar(); return self; }; /** * Release session group user lock files. * * @return {UserManager} */ self.releaseUserSessionGroup = function () { // Get Data let oUsers = self.getStorageVar(); // Get Session Group & Release locks //let sRunIdLockPath = `${self._sLockFolderPath}/${oUsers.runId}`; // @TODO: [X] LockManager lockExists() //if (self.fs().exists(sRunIdLockPath)) { if (self._lockManager.isLockExists(oUsers.runId)) { //let sSessionGroup = self.fileGetContent(sRunIdLockPath); let sSessionGroup = self._lockManager.getLockContent(oUsers.runId); //let sSessionGroupLockFile = `${self._sLockFolderPath}/${sSessionGroup}`; // @TODO: [X] LockManager lockExists() //if (self.fs().exists(sSessionGroupLockFile)) { if (self._lockManager.isLockExists(sSessionGroup)) { //self.fs().delete(sSessionGroupLockFile); self._lockManager.release(sSessionGroup); self.logger().message(`Session group '${sSessionGroup}' has been released successfully.`); } //self.fs().delete(sRunIdLockPath); self._lockManager.release(oUsers.runId); self.logger().message(`Releasing run execution lock file '${oUsers.runId}'.`); } // Clear & Save self._oUsers = { runId: null, runError: false }; // Save Data self.saveStorageVar(); return self; }; return self; } module.exports = UserManager;