// Java includes importPackage(Packages.de.elo.ix.client); // JavaScript includes //@include lib_Class.js /** * Utility functions for ACL processing. * * The functions `addRights` and `removeRights` apply changes to an objects ACL items. * The `restore` function can restore the original rights if needed, and if there was a call to one of the before mentioned functions with a store command. * * ## Function parameters * For examples see {@link sol.common.AclUtils#addRights addRights} and {@link sol.common.AclUtils#removeRights removeRights} * * ### objId * ObjId of the Object which should be edited. If there is a `recursive` command (see config parameter) this is the starting point from which all sub-elements will ne processed. * * ### users (only `addRights` and `removeRights`) * This is an Array with user names or user objects (or a mix of both). * If there are valid usernames specified, only those users ACL items will be altered. * If this contains objects they need to have a `name` property and may have an additional `rights` object. E.g.: * * { * users: [ * { name: "AMustermann", rights: { r: true, w: true } }, * { name: "BMustermann", rights: { r: true, w: true, d: true } }, * { name: "CMustermann" }, // will get the fallback access rights * "DMustermann" // will get the fallback access rights * ] * } * * If this parameter is undefined or an empty Array, all existing ACL items will be adjusted. * * ### rights (only `addRights` and `removeRights`) * This Object specifies, which rights should be added/removed. * Both following forms are valid: * * { r: true, w: true, d: false, e: false, l: false, p: false} * { read: false, write: true, del: true, edit: true, list: true. perm: true} * * The `addRights` function will add all right, flaged with `true`, while `removeRights` will remove all rights flaged with `true`. * * ### config * This Object contains additional processing information. * Currently the following parameters are supported: * * - recursive: if `true`, all sub-elements will be processed * - storeAcl: Object which defines the store path: `{ type: "MAP", key: "OLD_ACL" }`. Currently only map fields are supported. * - asAdmin: if `true`, the rights will be altered in admin context. * * ## Compatibility * All usage of the permission right ('p' or 'perm') is only supported in ELO12 and later. * * @author PZ, ELO Digital Office GmbH * @version 1.06.000 * * @elojc * @eloas * @eloix * @requires sol.common.SordUtils * @requires sol.common.RepoUtils * @requires sol.common.UserUtils * @requires sol.common.JsonUtils * @requires sol.common.ObjectFormatter * @requires sol.common.Template * @requires sol.common.AsyncUtils */ sol.define("sol.common.AclUtils", { singleton: true, /** * @private * @property */ sordZ: SordC.mbMin, /** * Adds rights to an archive entry/entries. * * The following example grants read, write and permission rights to the users "baum" and "renz" on exactly one object * * sol.common.AclUtils.addRights( * "4711", * ["baum", "renz"], * { r: true, w: true, d: false, e: false, l: false, p: true}, * { } * ); * * @param {String} objId The object which should be edited (if config has a `recursive` flag set to `true`, this object will be the starting point) * @param {String[]|Object[]} users If this contains strings, they serve as user names. If it contains objects, they have to contain a `name` property and an `rights` object. If empty, all existing ACL entries will be edited. * @param {Object} rights Object with flags for each right that should be added * @param {Object} config (optional) Additional configuration parameters * @param {Boolean} config.recursive (optional) Process children or not * @param {Object} config.storeAcl (optional) See example * @param {Boolean} config.asAdmin (optional) If `true` and admin context is available it will be used to perform the task */ addRights: function (objId, users, rights, config) { var me = this; me.logger.enter("addRights", arguments); me.editRights(objId, users, rights, config, me.addSordRights); me.logger.exit("addRights"); }, /** * Removes rights from an archive entry/entries. * * The following example will remove all rights (exept for read) for all users, having access to the object (and all sub-objects) * and store the original right to an map field (OLC_ACL) for a later restore. * * sol.common.AclUtils.removeRights( * "4711", * [], * { read: false, write: true, del: true, edit: true, list: true, perm: true }, * { recursive: true, storeAcl: { type: "MAP", key: "OLD_ACL" } } * ); * * @param {String} objId The object which should be edited (if config has a `recursive` flag set to `true`, this object will be the starting point) * @param {String[]|Object[]} users If this contains strings, they serve as user names. If it contains objects, they have to contain a `name` property and an `rights` object. If empty, all existing ACL entries will be edited. * @param {Object} rights Object with flags for each right that should be removed * @param {Object} config (optional) Additional configuration parameters * @param {Boolean} config.recursive (optional) Process children or not * @param {Object} config.storeAcl (optional) See example * @param {Boolean} config.asAdmin (optional) If `true` and admin context is available it will be used to perform the task */ removeRights: function (objId, users, rights, config) { var me = this; me.logger.enter("removeRights", arguments); // for backwards compatibility: remove p-right if nothing is specified if (!rights.hasOwnProperty("perm") && !rights.hasOwnProperty("p")) { rights.p = true; } me.editRights(objId, users, rights, config, me.removeSordRights); me.logger.exit("removeRights"); }, /** * Sets rights to an archive entry/entries. * * The following example grants read, write and permission rights to the users "baum" and "renz" on exactly one object * * sol.common.AclUtils.setRights( * "4711", * ["baum", "renz"], * { r: true, w: true, d: false, e: false, l: false, p: true}, * { } * ); * * @param {String} objId The object which should be edited (if config has a `recursive` flag set to `true`, this object will be the starting point) * @param {String[]|Object[]} users If this contains strings, they serve as user names. If it contains objects, they have to contain a `name` property and an `rights` object. If empty, all existing ACL entries will be edited. * @param {Object} rights Object with flags for each right that should be added * @param {Object} config (optional) Additional configuration parameters * @param {Boolean} config.recursive (optional) Process children or not * @param {Object} config.storeAcl (optional) See example * @param {Boolean} config.asAdmin (optional) If `true` and admin context is available it will be used to perform the task */ setRights: function (objId, users, rights, config) { var me = this; me.logger.enter("setRights", arguments); me.editRights(objId, users, rights, config, me.setSordRights); me.logger.exit("setRights"); }, /** * Restores (saved) rights for an archive entry/entries. * * The following example will restore all rights which where stored to the specified map field. * * sol.common.AclUtils.restoreRights( * "4711", * { recursive: true, storeAcl: { type: "MAP", key: "OLD_ACL" } } * ); * * @param {String} objId The object which should be edited (if config has a `recursive` flag set to `true`, this object will be the starting point) * @param {Object} config (optional) Additional configuration parameters * @param {Boolean} config.recursive (optional) Process children or not * @param {Object} config.storeAcl (optional) See example * @param {Boolean} config.asAdmin (optional) If `true` and admin context is available it will be used to perform the task */ restoreRights: function (objId, config) { var me = this, elements; me.logger.enter("restoreRights", arguments); if (config && config.storeAcl) { elements = me.retrieveElements(objId, config.recursive, config.asAdmin, config); elements.forEach(function (sord) { me.restoreSordRights(sord, config); }); } me.logger.exit("restoreRights"); }, /** * @private * @param {String} objId * @param {Array} users * @param {Object} rights * @param {Object} config * @param {Function} combineAclFunction */ editRights: function (objId, users, rights, config, combineAclFunction) { var me = this, accessCode, asAdmin, recursive, storeAcl, userAcl, elements; accessCode = me.createAccessCode(rights); asAdmin = (config && config.asAdmin === true) ? true : false; recursive = (config && config.recursive === true) ? true : false; storeAcl = (config && config.storeAcl) ? config.storeAcl : null; userAcl = me.retrieveUserAcl(users, accessCode, asAdmin); elements = me.retrieveElements(objId, recursive, asAdmin, config); elements.forEach(function (sord) { me.editSordRights(sord, { combineAclFunction: combineAclFunction, newAclList: userAcl, accessCode: accessCode, storeAcl: storeAcl, asAdmin: asAdmin }); }); }, /** * @private * @param {String[]|Object[]} users If this contains strings, they serve as user names. If it contains objects, they have to contain a `name` property and an `rights` object * @param {Number} accessCode * @param {Boolean} asAdmin * @return {de.elo.ix.client.AclItem[]} */ retrieveUserAcl: function (users, accessCode, asAdmin) { var me = this, connection, tmpAccessCode, userNames, userAcl, userInfos, userInfo, i, max; connection = ((asAdmin === true) && (typeof ixConnectAdmin !== "undefined")) ? ixConnectAdmin : ixConnect; tmpAccessCode = null; if (users && users.length > 0) { userAcl = []; userNames = users.map(function (user) { return (user && user.hasOwnProperty && user.hasOwnProperty("name")) ? user.name : user + ""; }); userInfos = connection.ix().checkoutUsers(userNames, CheckoutUsersC.BY_IDS, LockC.NO); for (i = 0, max = userInfos.length; i < max; i++) { userInfo = userInfos[i]; if (users[i] && users[i].hasOwnProperty && users[i].hasOwnProperty("rights")) { tmpAccessCode = me.createAccessCode(users[i].rights); } userAcl.push(me.createAclItemFromUserInfo(userInfo, tmpAccessCode || accessCode)); tmpAccessCode = null; } return userAcl; } return null; }, /** * @private * @param {Object} andGroup An and-group definition (see {@link #changeRightsInBackground}) * @param {Number} defaultAccessCode * @param {Boolean} asAdmin * @return {de.elo.ix.client.AclItem} */ retrieveAndGroupAcl: function (andGroup, defaultAccessCode, asAdmin) { var me = this, connection, groupsAccessCode, groupNames, groupInfos, userInfo, i, max, aclGroupsItem, idNameItem, idNames; connection = ((asAdmin === true) && (typeof ixConnectAdmin !== "undefined")) ? ixConnectAdmin : ixConnect; if (andGroup.groups && (andGroup.groups.length > 1)) { groupNames = andGroup.groups.map(function (group) { return (group && group.hasOwnProperty && group.hasOwnProperty("name")) ? group.name : group; }); groupInfos = connection.ix().checkoutUsers(groupNames, CheckoutUsersC.BY_IDS, LockC.NO); if (andGroup.rights) { groupsAccessCode = me.createAccessCode(andGroup.rights); } aclGroupsItem = new AclItem(); aclGroupsItem.id = groupInfos[0].id; aclGroupsItem.type = AclItemC.TYPE_GROUP; aclGroupsItem.access = groupsAccessCode || defaultAccessCode; idNames = []; for (i = 1, max = groupInfos.length; i < max; i++) { userInfo = groupInfos[i]; // eslint-disable-next-line no-undef idNameItem = new IdName(); idNameItem.name = String(userInfo.name); idNameItem.guid = String(userInfo.guid); idNameItem.id = String(userInfo.id); idNames.push(idNameItem); } aclGroupsItem.andGroups = idNames; return aclGroupsItem; } return null; }, /** * @private * @param {de.elo.ix.client.Sord} sord * @param {Number} accessCode * @return {de.elo.ix.client.AclItem[]} */ retrieveSordAcl: function (sord, accessCode) { var me = this, sordAcl, i, aclItem; me.logger.enter("retrieveSordAcl", arguments); sordAcl = []; if (sord && (sord instanceof Sord)) { for (i = 0; i < sord.aclItems.length; i++) { aclItem = sord.aclItems[i]; sordAcl.push(me.createAclItemFromAcl(aclItem, accessCode)); } } me.logger.exit("retrieveSordAcl", sordAcl); return sordAcl; }, /** * @private * @param {String} objId * @param {Boolean} recursive * @param {Boolean} asAdmin * * @param {*} config * @return {de.elo.ix.client.Sord[]} */ retrieveElements: function (objId, recursive, asAdmin, config) { var me = this, connection = ((asAdmin === true) && (typeof ixConnectAdmin !== "undefined")) ? ixConnectAdmin : ixConnect, elements = [connection.ix().checkoutSord(objId, me.sordZ, LockC.NO)], createFindChildrenConfig = function (aConfig) { var findConfig = sol.common.ObjectUtils.clone(aConfig.findChildren || {}); findConfig.includeFolders = typeof findConfig.includeFolders != "undefined" ? findConfig.includeFolders : true; findConfig.includeDocuments = typeof findConfig.includeDocuments != "undefined" ? findConfig.includeDocuments : true; findConfig.sordZ = me.sordZ; findConfig.recursive = typeof findConfig.recursive != "undefined" ? findConfig.recursive : true; findConfig.level = typeof findConfig.level != "undefined" ? findConfig.level : -1; return findConfig; }; if (recursive === true) { return elements.concat(sol.common.RepoUtils.findChildren( objId, createFindChildrenConfig(config), connection ) || []); } return elements; }, /** * @private * @param {de.elo.ix.client.AclItem[]} oldAclList * @param {de.elo.ix.client.AclItem[]} newAclList * @param {Boolean} asAdmin * @return {de.elo.ix.client.AclItem[]} */ addSordRights: function (oldAclList, newAclList, asAdmin) { var connection, _result; connection = ((asAdmin === true) && (typeof ixConnectAdmin !== "undefined")) ? ixConnectAdmin : ixConnect; _result = connection.ix().combineAcl(oldAclList, newAclList, null).sum; return _result; }, /** * @private * @param {de.elo.ix.client.AclItem[]} _oldAclList is not used here * @param {de.elo.ix.client.AclItem[]} newAclList * @param {Boolean} asAdmin * @return {de.elo.ix.client.AclItem[]} */ setSordRights: function (_oldAclList, newAclList, asAdmin) { var connection; connection = ((asAdmin === true) && (typeof ixConnectAdmin !== "undefined")) ? ixConnectAdmin : ixConnect; return connection.ix().combineAcl(newAclList, null, null).sum; }, /** * @private * @param {de.elo.ix.client.AclItem[]} oldAclList * @param {de.elo.ix.client.AclItem[]} newAclList * @param {Boolean} asAdmin * @return {de.elo.ix.client.AclItem[]} */ removeSordRights: function (oldAclList, newAclList, asAdmin) { var connection, _result; connection = ((asAdmin === true) && (typeof ixConnectAdmin !== "undefined")) ? ixConnectAdmin : ixConnect; _result = connection.ix().combineAcl(oldAclList, newAclList, null).difference; return _result; }, /** * @private * @param {de.elo.ix.client.Sord[]} sord * @param {Object} params */ editSordRights: function (sord, params) { var me = this, connection, oldAclList, oldAclString; connection = ((params.asAdmin === true) && (typeof ixConnectAdmin !== "undefined")) ? ixConnectAdmin : ixConnect; oldAclList = sord.aclItems; oldAclString = sord.acl; if (params.storeAcl) { switch (params.storeAcl.type) { case "MAP": connection.ix().checkinMap(MapDomainC.DOMAIN_SORD, sord.id, sord.id, [new KeyValue(params.storeAcl.key, oldAclString)], LockC.NO); break; case "GRP": throw "store ACL to group is not implemented yet"; default: throw "unkown field type"; } } if (!params.newAclList) { params.newAclList = me.retrieveSordAcl(sord, params.accessCode); } sord.aclItems = params.combineAclFunction(oldAclList, params.newAclList, params.asAdmin); connection.ix().checkinSord(sord, me.sordZ, LockC.NO); }, /** * @private * @param {de.elo.ix.client.Sord} sord * @param {Object} params */ restoreSordRights: function (sord, params) { var me = this, connection, mapItems; connection = ((params.asAdmin === true) && (typeof ixConnectAdmin !== "undefined")) ? ixConnectAdmin : ixConnect; if (params.storeAcl.type === "MAP") { mapItems = connection.ix().checkoutMap(MapDomainC.DOMAIN_SORD, sord.id, [params.storeAcl.key], LockC.NO).items; if (mapItems && mapItems.length === 1) { sord.acl = mapItems[0].value; sord.aclItems = null; connection.ix().checkinSord(sord, me.sordZ, LockC.NO); connection.ix().deleteMap(MapDomainC.DOMAIN_SORD, sord.id, [params.storeAcl.key], LockC.NO); } } }, /** * @private * @param {de.elo.ix.client.UserInfo} userInfo * @param {Number} accessCode * @return {de.elo.ix.client.AclItem} */ createAclItemFromUserInfo: function (userInfo, accessCode) { var aclItem; aclItem = new AclItem(); aclItem.id = userInfo.id; aclItem.type = (userInfo.type == UserInfoC.TYPE_GROUP) ? AclItemC.TYPE_GROUP : AclItemC.TYPE_USER; aclItem.access = accessCode; return aclItem; }, /** * @private * @param {de.elo.ix.client.AclItem} aclItem * @param {Number} accessCode * @return {de.elo.ix.client.AclItem} */ createAclItemFromAcl: function (aclItem, accessCode) { var me = this, newAclItem, i, newAndGroups, idName, newIdName; me.logger.enter("createAclItemFromAcl", arguments); newAclItem = new AclItem(); newAclItem.id = aclItem.id; newAclItem.type = aclItem.type; newAclItem.access = accessCode; if (aclItem.andGroups && (aclItem.andGroups.length > 0)) { newAndGroups = []; for (i = 0; i < aclItem.andGroups.length; i++) { idName = aclItem.andGroups[i]; // eslint-disable-next-line no-undef newIdName = new IdName(); newIdName.id = idName.id; newIdName.name = idName.name; newAndGroups.push(newIdName); } newAclItem.andGroups = newAndGroups; } me.logger.exit("createAclItemFromAcl", newAclItem); return newAclItem; }, /** * @private * @param {Object} rights * @return {Number} */ createAccessCode: function (rights) { var me = this, code; code = 0; if (!rights) { throw "Rights are empty"; } if ((rights.read === true) || (rights.r === true)) { code |= AccessC.LUR_READ; } if ((rights.write === true) || (rights.w === true)) { code |= AccessC.LUR_WRITE; } if ((rights.del === true) || (rights.d === true)) { code |= AccessC.LUR_DELETE; } if ((rights.edit === true) || (rights.e === true)) { code |= AccessC.LUR_EDIT; } if ((rights.list === true) || (rights.l === true)) { code |= AccessC.LUR_LIST; } if (((rights.perm === true) || (rights.p === true)) && (me.isAccessCodePermissionAvailable())) { code |= AccessC.LUR_PERMISSION; } return code; }, /** * Is the access code `permission` available * Check must be compatible with Rhino, Nashorn and GraalVM * * @returns {Boolean} */ isAccessCodePermissionAvailable: function () { var me = this, accessC, accessCClass; if (typeof me.accessCodePermissionAvailable == "undefined") { accessC = new AccessC; accessCClass = accessC.getClass(); me.accessCodePermissionAvailable = me.hasClassField(accessCClass, "LUR_PERMISSION"); } return me.accessCodePermissionAvailable; }, /** * @private * @param {java.lang.Class} clazz * @param {String} fieldName * @return {Boolean} Has class field */ hasClassField: function (clazz, fieldName) { var fields, i, field; fields = clazz.getDeclaredFields(); for (i = 0; i < fields.length; i++) { field = fields[i]; if (field.name == fieldName) { return true; } } return false; }, /** * Sets or adds the rights by a background IX thread. * * Examples: * * var jobState = sol.common.AclUtils.changeRightsInBackground("ARCPATH:/AclTest/Acl1", { inherit: true, users: ["weiler", {name: "zipfel", rights:{r: true, w: true, p: true}}], rights: { r: true } }); * var jobState = sol.common.AclUtils.changeRightsInBackground("ARCPATH:/AclTest/Acl1", { mode: "SET", users: ["zipfel"], rights: { r: true } }); * var jobState = sol.common.AclUtils.changeRightsInBackground("ARCPATH:/AclTest/Acl1", { mode: "SET", users: ["weiler", { name: "zipfel", rights:{ r: true, w: true } }], rights: { r: true }, andGroups: { groups: ["Pubsec.Registratur", { name: "Pubsec.Sachbearbeiter" }], rights: { d: true } } }); * var jobState = sol.common.AclUtils.changeRightsInBackground("ARCPATH:/AclTest/Acl1", { mode: "ADD", users: [{ "type": "GRP", "key": "CONTRACT_RESPONSIBLE", "rights": { "r": true, "w": true, "d": false, "e": false, "l": false } }] }); * var jobState = sol.common.AclUtils.changeRightsInBackground("ARCPATH:/AclTest/Acl1", { mode: "ADD", users: [{ "type": "GRP", "key": "CONTRACT_RESPONSIBLE", "mode": "SUPERVISOR", "supervisorLevel": 0, "rights": { "r": true, "w": true, "d": false, "e": false, "l": false } }] }); * * Example with and-groups * (sets an and-group with the groups 'GroupA', 'GroupB' and the group from the CONTRACT_RESPONSIBLE field with read only, * as well as an and-group with the groups 'GroupX' and 'GroupY' with write access): * * var jobState = sol.common.AclUtils.changeRightsInBackground("ARCPATH:/AclTest/Acl1", { * mode: "SET", * andGroups: [ * { groups: ["GroupA", "GroupB", { type: "GRP", key: "CONTRACT_RESPONSIBLE" }] }, * { groups: ["GroupX", "GroupY"], rights: { r: true, w: true } } * ], * rights: { r: true } // default rights * }); * * User or group names can be created using handlbars syntax (if the value will be read from an indexfield, that value could also contain handlebars syntax): * * var jobState = sol.common.AclUtils.changeRightsInBackground("ARCPATH:/AclTest/Acl1", { * mode: "ADD", * users: [ * "LEGAL_DEP_{{sord.objKeys.CONTRACT_DEPARTMENT}}", * { name: "CONTROLLING_{{sord.objKeys.CONTRACT_DEPARTMENT}}", rights: { r: true, w: true } } * ], * rights: { r: true } // default rights * }); * * @param {String|String[]} objId Object ID of the object which will be changed or an array with start ids where each will be prosessed. If an array of ids is used, the parameter `config.srcObjId` is mandatory. * @param {Object} config Configuration * @param {String} [config.mode="ADD"] "ADD", "SET" or "REMOVE" rights * @param {Object} config.inherit Inheritance configuration * @param {Boolean} config.inherit.fromDirectParent Inherit the ACL from the direct parent (default) * @param {Boolean} config.inherit.aclSrcObjId Source object ID for the ACL inheritance * @param {String[]} config.inherit.solutionObjectTypes Solution object types to find the ACL source object in the hierachy * @param {String[]|Object[]} config.users If this contains strings, they serve as user names. If it contains objects, they have to contain a `name` property and an `rights` object * @param {Object[]} config.andGroups An array with and-group definitions * @param {String[]|Object[]} config.andGroups.groups The groups contained in an and-group. Defined in the same way as `config.users` (but without the `rights` property). * If the `groups` array contains less than two entries the that and-group definition will be ignored. * @param {Object[]} config.andGroups.rights The rights of an and-group if deviant from fallback (`config.rights`) * @param {String} config.srcObjId If set, the user configurations will be read from this object instead of `objId`. Only mandatory if `objId` is an array. * @param {Object} config.rights Additional rights, e.g. { read: false, write: true, del: true, edit: true, list: true, perm: true } * @param {Boolean} [config.recursive=true] If true the ACL of the children will also be changed. Default is true. * @param {Boolean} [config.dontWait=false] Don't wait for the background process. Default is false (synchronous). * @param {String} config.flowId Flow ID to determine the values of flow map fields * @return {de.elo.ix.client.JobState} */ changeRightsInBackground: function (objId, config) { var me = this, logObj = { objId: String(objId), config: config }, newAclItems = [], conn = (typeof ixConnectAdmin !== "undefined") ? ixConnectAdmin : ixConnect, processObjIds, metadataObjId, defaultAccessCode, jobState; me.logger.enter("changeRightsInBackground", logObj); config = config || {}; me.checkPreconditions(objId, config); processObjIds = (sol.common.ObjectUtils.isArray(objId)) ? objId : [objId]; metadataObjId = config.srcObjId || objId; me.initializeRights(newAclItems, metadataObjId, config, conn); defaultAccessCode = me.createAccessCode(config.rights); me.appendInheritedAcl(newAclItems, metadataObjId, config, conn); me.appendUserAcl(newAclItems, metadataObjId, config, defaultAccessCode); me.appendAndGroupAcl(newAclItems, metadataObjId, config, defaultAccessCode); if (me.canExecute(config.mode, newAclItems)) { jobState = me.executeBackgroundAclJob(conn, processObjIds, config, newAclItems); } else { jobState = null; me.logger.warn("no acl items to set/add/remove -> skip processing"); } me.logger.exit("changeRightsInBackground", jobState); return jobState; }, /** * @private * Checks the preconditions and throws an exception if they are not meet. * @param {String|String[]} objId * @param {Object} config */ checkPreconditions: function (objId, config) { if (!objId) { throw "Object ID is empty"; } if (sol.common.ObjectUtils.isArray(objId) && !config.srcObjId) { throw "If 'objId' is configured as array, the config paramater 'srcObjId' is mandatory."; } }, /** * @private * Checks and initializes the rights config. * @param {de.elo.ix.client.AclItem[]} newAclItems * @param {String} objId * @param {Object} config * @param {de.elo.ix.client.IXConnection} conn */ initializeRights: function (newAclItems, objId, config, conn) { var sord; if (config.mode == "REMOVE") { config.rights = config.rights || { read: false, write: true, del: true, edit: true, list: true, perm: true }; if (!config.users) { sord = conn.ix().checkoutSord(objId, new SordZ(SordC.mbAclItems), LockC.NO); newAclItems = sord.aclItems; } } else { config.rights = config.rights || { read: true, write: true, del: true, edit: true, list: true, perm: true }; if (!config.users && !config.andGroups && !config.inherit) { throw "Users, andGroups or inheritance must be set"; } } }, /** * @private * Appends the inherited ACL items to the `newAclItems` array as configured. * @param {de.elo.ix.client.AclItem[]} newAclItems * @param {String} objId * @param {Object} config * @param {de.elo.ix.client.IXConnection} conn */ appendInheritedAcl: function (newAclItems, objId, config, conn) { var aclSrcSord, sord, i, aclItem; if (config.inherit) { if (config.inherit.aclSrcObjId) { aclSrcSord = conn.ix().checkoutSord(config.inherit.aclSrcObjId, new SordZ(SordC.mbAclItems), LockC.NO); } else if (config.inherit.solutionObjectTypes) { aclSrcSord = sol.common.RepoUtils.findObjectTypeInHierarchy(objId, config.inherit.solutionObjectTypes, { connection: conn }); } else { sord = conn.ix().checkoutSord(objId, SordC.mbMin, LockC.NO); if (sord.parentId > 0) { aclSrcSord = conn.ix().checkoutSord(sord.parentId, new SordZ(SordC.mbAclItems), LockC.NO); } } if (aclSrcSord) { for (i = 0; i < aclSrcSord.aclItems.length; i++) { aclItem = aclSrcSord.aclItems[i]; newAclItems.push(aclItem); } } } }, /** * @private * Appends the user ACL items to the `newAclItems` array as configured. * @param {de.elo.ix.client.AclItem[]} newAclItems * @param {String} objId * @param {Object} config * @param {Number} defaultAccessCode */ appendUserAcl: function (newAclItems, objId, config, defaultAccessCode) { var me = this, users, userAcls; users = me.preprocessUsers(objId, config.users, config); userAcls = me.retrieveUserAcl(users, defaultAccessCode); if (userAcls) { userAcls.forEach(function (userAcl) { newAclItems.push(userAcl); }); } }, /** * @private * Appends the and-group ACL items to the `newAclItems` array as configured. * @param {de.elo.ix.client.AclItem[]} newAclItems * @param {String} objId * @param {Object} config * @param {Number} defaultAccessCode */ appendAndGroupAcl: function (newAclItems, objId, config, defaultAccessCode) { var me = this; if (config.andGroups && (config.andGroups.length > 0)) { config.andGroups.forEach(function (andGroup) { var andGroupAcl; andGroup.groups = me.preprocessUsers(objId, andGroup.groups, config); andGroupAcl = me.retrieveAndGroupAcl(andGroup, defaultAccessCode); if (andGroupAcl) { newAclItems.push(andGroupAcl); } }); } }, /** * @private * Checks, if the background processing should be executed. * * If mode is `SET` or `REPLACE` this always returns `true`, else it checks if there are acl items. * @param {String} mode * @param {Object[]} aclItems * @return {Boolean} */ canExecute: function (mode, aclItems) { if ((mode == "SET") || (mode == "REPLACE")) { return true; } return aclItems && (aclItems.length > 0); }, /** * @private * Executes the background processing of the rights. * @param {de.elo.ix.client.IXConnection} conn * @param {String[]} startIds * @param {Object} config * @param {de.elo.ix.client.AclItem[]} newAclItems * @return {de.elo.ix.client.JobState} */ executeBackgroundAclJob: function (conn, startIds, config, newAclItems) { var me = this, procInfo, navInfo, jobState; me.logger.enter("executeBackgroundAclJob"); procInfo = new ProcessInfo(); procInfo.desc = (config.mode || "ADD") + " ACL"; procInfo.errorMode = ProcessInfoC.ERRORMODE_SKIP_PROCINFO; procInfo.procAcl = new ProcessAcl(); if ((config.mode == "SET") || (config.mode == "REPLACE")) { procInfo.procAcl.setAclItems = newAclItems; } else if (config.mode == "REMOVE") { procInfo.procAcl.subAclItems = newAclItems; } else { procInfo.procAcl.addAclItems = newAclItems; } navInfo = new NavigationInfo(); navInfo.startIDs = startIds; navInfo.maxDepth = (config.recursive === false) ? 1 : 0; jobState = conn.ix().processTrees(navInfo, procInfo); if (!config.dontWait && !me.ixExecutesBackgroundJobsSynchronous(conn)) { jobState = sol.common.AsyncUtils.waitForJob(jobState.jobGuid, { connection: conn }); } me.logger.exit("executeBackgroundAclJob", jobState); return jobState; }, /** * @private * Checks if the IX version processes background jobs synchronous. * This is true for IX version higher than '9.18.060', '10.18.060' and '11.01.000'. * @param {de.elo.ix.client.IXConnection} conn The connection wich is used to determine the IX version. * @return {Boolean} */ ixExecutesBackgroundJobsSynchronous: function (conn) { return sol.common.RepoUtils.checkVersions(conn.clientVersion, ["9.18.060", "10.18.060", "11.01.000"]); }, /** * Preprocesses users. * Retrieves user names from index fields. * If a user name or the retrieved value contains handlebars syntax, the sord will be applied to that string. * @param {String} objId Object ID * @param {String[]|Number[]|Object[]} users Array of user defintions. all Types can be mixed. * @param {Object} params Parameters * @param {String} params.flowId Workflow ID * @return {Object[]} * * # Example of the parameter 'users': * [ * "mustermannm", * 23, * { "type": "GRP", "key": "CONTRACT_RESPONSIBLE", "rights": { "r": true, "w": true, "d": false, "e": false, "l": false, "p": true } }, * { "type": "GRP", "key": "CONTRACT_DEPARTMENT", "rights": { "r": true, "w": true, "d": false, "e": false, "l": false, "p": true } }, * { "type": "GRP", "key": "CONTRACT_PROCUREMENT", "rights": { "r": true, "w": true, "d": false, "e": false, "l": false, "p": false } }, * { "name": "LEGAL_DEP_{{sord.objKeys.CONTRACT_DEPARTMENT}}", "rights": { "r": true, "w": false, "d": false, "e": false, "l": false, "p": false } } * ] */ preprocessUsers: function (objId, users, params) { var me = this, ctxSord = { objId: objId }, conn, userCfgs; conn = (typeof ixConnectAdmin !== "undefined") ? ixConnectAdmin : ixConnect; userCfgs = []; if (users && (users.length > 0)) { users = JSON.parse(sol.common.JsonUtils.stringifyAll(users)); users.forEach(function (user) { var userNames = [], rawUserNames, rawUserName, userName, i, supervisorLevel, userInfos, sord; if (sol.common.ObjectUtils.isString(user) || sol.common.ObjectUtils.isNumber(user)) { user = me.replaceUserNamePlaceholders(String(user), ctxSord, conn, params.flowId); if (user) { userCfgs.push(user); } } else if (!user.name && user.type && user.key) { try { sord = me.enrichContextSord(ctxSord, false, conn, params.flowId).sord; if ((user.type == "WFMAP") && (params.flowId)) { rawUserName = sol.common.WfUtils.getWfMapValue(params.flowId, user.key); rawUserNames = (rawUserName) ? [rawUserName] : []; } else { rawUserNames = sol.common.SordUtils.getValues(sord, user); } supervisorLevel = user.supervisorLevel || 0; rawUserNames = rawUserNames || []; for (i = 0; i < rawUserNames.length; i++) { rawUserName = rawUserNames[i]; userName = rawUserName; if (user.mode && (user.mode.toUpperCase() == "SUPERVISOR")) { userName = sol.common.UserUtils.getSupervisorOfLevel(rawUserName, supervisorLevel); if (!userName) { me.logger.debug("Can't find supervisor: userName={0}", rawUserName); } } if (userName) { userNames.push(userName); } } if (userNames.length > 0) { userInfos = ixConnect.ix().checkoutUsers(userNames, CheckoutUsersC.BY_IDS, LockC.NO); } if (userInfos && (userInfos.length > 0)) { userInfos.forEach(function (userInfo) { var tmpUser = JSON.parse(sol.common.JsonUtils.stringifyAll(user)); // copy current user configuration for each user in the list tmpUser.name = me.replaceUserNamePlaceholders(userInfo.name, ctxSord, conn, params.flowId); if (tmpUser) { userCfgs.push(tmpUser); } }); } } catch (ignore) { me.logger.debug("error determining user(s)", ignore); } } else { user.name = me.replaceUserNamePlaceholders(user.name, ctxSord, conn, params.flowId); if (user && user.name) { userCfgs.push(user); } } }); me.logger.debug(["preprocessUsers(): resolvedUsers={0}", JSON.stringify(userCfgs)]); return userCfgs; } }, /** * @private * Replace user name place holders: * * - If `userName` equals '$CURRENTUSER' the current session user will be used. * - If `username` contains handlebars syntax, the sord will be applied to it * * @param {String} userName * @param {Object} ctxSord (optional) only used if `userName` contains handlebars syntax * @param {de.elo.ix.client.IXConnection} conn (optional) only used if `userName` contains handlebars syntax * @param {String} flowId (optional) * @return {String} Username */ replaceUserNamePlaceholders: function (userName, ctxSord, conn, flowId) { var me = this, tplSord; userName = (userName || "") + ""; if (userName == "$CURRENTUSER") { return String(ixConnect.loginResult.user.name); } if (userName.indexOf("{{") > -1) { tplSord = me.enrichContextSord(ctxSord, true, conn, flowId).tplSord; return sol.create("sol.common.Template", { source: userName }).apply(tplSord); } return userName; }, /** * @private * Prefills the context object with sord and if requested with a template sord. * @param {Object} ctxSord * @param {Boolean} inclTplSord * @param {de.elo.ix.client.IXConnection} conn * @param {String} flowId (optional) * @return {Object} The ctxSord itself after enrichment */ enrichContextSord: function (ctxSord, inclTplSord, conn, flowId) { if (!ctxSord.sord) { if (!ctxSord.objId) { throw "Object ID is empty"; } ctxSord.sord = conn.ix().checkoutSord(ctxSord.objId, SordC.mbAllIndex, LockC.NO); } if (inclTplSord && !ctxSord.tplSord) { ctxSord.tplSord = flowId ? sol.common.WfUtils.getTemplateSord(ctxSord.sord, flowId) : sol.common.SordUtils.getTemplateSord(ctxSord.sord); } return ctxSord; }, /** * Checks wether the users object contains the session user und the session user has already the effective rights * @param {Object} rightsConfig Rights configuration * @param {String} rightsConfig.objId Object ID * @param {Object} rightsConfig.rights Rights * @return {Boolean} */ containsSessionUserAndhasEffectiveRights: function (rightsConfig) { var me = this, currUserName, effectiveRights; if (rightsConfig && rightsConfig.objId && rightsConfig.rights && rightsConfig.users && (rightsConfig.users.length == 1) && sol.common.ObjectUtils.isString(rightsConfig.users[0])) { currUserName = ixConnect.loginResult.user.name; if (rightsConfig.users[0] == currUserName) { effectiveRights = me.hasEffectiveRights(rightsConfig.objId, rightsConfig.rights); return effectiveRights; } } return false; }, /** * Checks wether the current user has effective rights * @param {String|de.elo.ix.client.Sord} sord Object ID or Sord * @param {Object} params Parameters * @param {Object} params.rights Rights * @return {Boolean} */ hasEffectiveRights: function (sord, params) { var me = this, hasRights = false, accessCode; if (!sord) { throw "Object ID is empty"; } params = params || {}; params.rights = params.rights || { r: true, w: true, d: true, e: true, l: true, p: true }; try { if (!(sord instanceof Sord) || !sord.aclItems) { sord = ixConnect.ix().checkoutSord(sord, new SordZ(SordC.mbAclItems), LockC.NO); } accessCode = sol.common.AclUtils.getAccessCode(sord); hasRights = me.containsRights(accessCode, params.rights); } catch (ignore) { // ignore } return hasRights; }, accessCodes: { r: AccessC.LUR_READ, read: AccessC.LUR_READ, w: AccessC.LUR_WRITE, write: AccessC.LUR_WRITE, d: AccessC.LUR_DELETE, del: AccessC.LUR_DELETE, e: AccessC.LUR_EDIT, edit: AccessC.LUR_EDIT, l: AccessC.LUR_LIST, list: AccessC.LUR_LIST, p: new AccessC().LUR_PERMISSION, // 'new' is necessary, because it throws no exception if property does not exist (prior to ELO12) perm: new AccessC().LUR_PERMISSION // 'new' is necessary, because it throws no exception if property does not exist (prior to ELO12) }, /** * Checks wether the access code contains the requested rights * @param {Number} accessCode Access code * @param {Object} rights Rights * @return {Boolean} */ containsRights: function (accessCode, rights) { var me = this, rightKey, accessFlag; if (!accessCode || !rights) { return true; } for (rightKey in rights) { if (rights.hasOwnProperty(rightKey)) { accessFlag = me.accessCodes[rightKey]; if (accessFlag && ((accessCode & accessFlag) == 0)) { return false; } } } return true; }, /** * Returns an object with the access rights of the current user on a sord. * @param {de.elo.ix.client.Sord} sord * @returns {Object} */ getAccessRights: function (sord) { var accessCode; accessCode = sol.common.AclUtils.getAccessCode(sord); return sol.common.AclUtils.getAccessRightsByAccessCode(accessCode); }, /** * Returns an object with the access rights of the given accessCode. * @param {number} accessCode * @returns {Object} */ getAccessRightsByAccessCode: function (accessCode) { var accessRights = { r: sol.common.AclUtils.containsRights(accessCode, { r: true }), w: sol.common.AclUtils.containsRights(accessCode, { w: true }), d: sol.common.AclUtils.containsRights(accessCode, { d: true }), e: sol.common.AclUtils.containsRights(accessCode, { e: true }), l: sol.common.AclUtils.containsRights(accessCode, { l: true }), p: sol.common.AclUtils.containsRights(accessCode, { p: true }) }; return accessRights; }, /** * Returns the access code of the given sord object * @param {String|de.elo.ix.client.Sord} sord Object ID or Sord * @param {Object} params Parameters * @param {de.elo.ix.client.IXConnection} params.connection Index server connection * @returns {number} access code */ getAccessCode: function (sord, params) { var aclAccessInfo, conn, access; params = params || {}; params.connection = params.connection || ixConnect; conn = params.connection; aclAccessInfo = new AclAccessInfo(); if (!(sord instanceof Sord) || !sord.aclItems) { sord = conn.ix().checkoutSord(sord, new SordZ(SordC.mbAclItems), LockC.NO); } aclAccessInfo.aclItems = sord.aclItems; access = conn.ix().getAclAccess(aclAccessInfo).access; return access; } });