// 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 */ 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"); }, /** * 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); 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); 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]; 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 * @return {de.elo.ix.client.Sord[]} */ retrieveElements: function (objId, recursive, asAdmin) { var me = this, connection, elements, children, i, max; connection = ((asAdmin === true) && (typeof ixConnectAdmin !== "undefined")) ? ixConnectAdmin : ixConnect; elements = []; elements.push(connection.ix().checkoutSord(objId, me.sordZ, LockC.NO)); if (recursive === true) { children = sol.common.RepoUtils.findChildren(objId, { includeFolders: true, includeDocuments: true, sordZ: me.sordZ, recursive: true, level: -1 }, connection); if (children && children.length > 0) { for (i = 0, max = children.length; i < max; i++) { elements.push(children[i]); } } } 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 * @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]; 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 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 ((typeof new AccessC().LUR_PERMISSION !== "undefined") && ((rights.perm === true) || (rights.p === true))) { // additional check is necessary if property does not exist (prior to ELO12) code |= AccessC.LUR_PERMISSION; } return code; }, /** * 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 } }] }); * * 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). * @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); aclSrcSord = conn.ix().checkoutSord(sord.parentId, new SordZ(SordC.mbAclItems), LockC.NO); } 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); 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); 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. * @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) { 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, userInfos, sord; if (sol.common.ObjectUtils.isString(user) || sol.common.ObjectUtils.isNumber(user)) { user = me.replaceUserNamePlaceholders(String(user), ctxSord, conn); userCfgs.push(user); } else if (!user.name && user.type && user.key) { try { sord = me.enrichContextSord(ctxSord, false, conn).sord; userNames = sol.common.SordUtils.getValues(sord, user); 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); userCfgs.push(tmpUser); }); } } catch (ignore) { me.logger.debug("error determining user(s)", ignore); } } else { user.name = me.replaceUserNamePlaceholders(user.name, ctxSord, conn); userCfgs.push(user); } }); 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 * @return {String} Username */ replaceUserNamePlaceholders: function (userName, ctxSord, conn) { var me = this, tplSord; if (userName == "$CURRENTUSER") { return String(ixConnect.loginResult.user.name); } if (userName.indexOf("{{") > -1) { tplSord = me.enrichContextSord(ctxSord, true, conn).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 * @return {Object} The ctxSord itself after enrichment */ enrichContextSord: function (ctxSord, inclTplSord, conn) { 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 = 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, aclAccessInfo, aclAccessResult, 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); } aclAccessInfo = new AclAccessInfo(); aclAccessInfo.aclItems = sord.aclItems; aclAccessResult = ixConnect.ix().getAclAccess(aclAccessInfo); accessCode = aclAccessResult.access; 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; } });