Skip to content
Snippets Groups Projects
process.js 21.7 KiB
Newer Older
import("system.datetime");
import("Sql_lib");
import("system.util");
import("system.logging");
import("system.text");
import("Util_lib");
import("system.db");
import("system.project");
import("system.translate");
import("system.tools");
import("system.project");
import("ActiveDirectoryXml_lib");

//TODO: add comments to methods

var ColumnMetadataProvider = (function () 
{
    
    var metadataInstance = {};
    function _init(pTable) {
        
        var columns = db.getColumns(pTable);
        var metaData = db.getColumnTypes(pTable, columns);
        return {
            columns: columns,
            types: metaData
        };
    }
    return {
        getInstanceFor: function(pTable) {
            if(!metadataInstance[pTable]) {
                metadataInstance[pTable] = _init(pTable);
            }
            return metadataInstance[pTable];
        }
    };
})();


function LdapImporter()
{
    //handler to actually do something based on decisions that where made earlier
    //these callback functions need to be set later
    
    /**
     * callbackFunction that needs to be defined; will receive the following params:
     * <br/>- entry: a DNentry of the ldap-entry that is missing in ADITO
     * <br/>- preparedEntryData: key-value pair of the entry where the key is the DB-field-information and the value is an object like this:
     * {
     *    "VALUE": <i>actual value that needs to be written into the database</i>,
     *    "ACTION": <i>description when the value has to be written (insert,update or both)</i>
     *  }; 
     */
    this.onUserMissingInADITO = null;
    
    /**
     * callbackFunction that needs to be defined; will receive the following params:
     * <br/>- usermodel: the usermodel as it is returned by toos.getUser that is missing in ldap but exists in ADITO
     */
    this.onUserMissingInLdap = null;
    
    /**
     * callbackFunction that needs to be defined; will receive the following params:
     * <br/>- entry: a DNentry object of the ldap-entry  
     * <br/>- preparedEntryData: key-value pair of the entry where the key is the DB-field-information and the value is an object like this:
     * {
     *    "VALUE": <i>actual value that needs to be written into the database</i>,
     *    "ACTION": <i>description when the value has to be written (insert,update or both)</i>
     *  }; 
     *  <br/>- usermodel: the usermodel as it is returned by toos.getUser that was modified
     */
    this.onUserModified = null;
    
    /**
     * callbackFunction that needs to be defined; will receive the following params:
     * <br/>- profile: the user profile that is requested (tools.PROFILE_TITLE, tools.PROFILE_FULL, etc.)
     * <br/>- entry: a DNentry of the user that needs to be searched
     */
    this.searchUserInADITO = null;
    
    this.dataMapping = {}//todo: maybe use map instead?
    this.custom = null;//space for custom holded elements that may be used in callback functions
    
    this.dn = null;
    this.filter = null;
    
    this.technicalUserTitel = null;
}

LdapImporter.prototype.getLdapConfig = function() 
{
    var ldapUserDN = project.getPreferenceValue("custom.AD.technicalUserDN");
    var ldapUserPW = project.getPreferenceValue("custom.AD.technicalUserPassword");
    var url = this.url;//TODO restore line from GIT
    var authMode = project.getPreferenceValue("custom.ldap.authentification.mode", "simple");
    var referral = "follow"; //follow, ignore, throw //TODO System.option?
    return {
        login: new LoginData(ldapUserDN, ldapUserPW, url, authMode, referral),
        dn: this.dn,
        filter: this.filter,
        scope: project.getPreferenceValue("custom.ldap.sync.scope", "subtree"),
        limits: project.getPreferenceValue("custom.ldap.sync.limits", 1000)
    };
};

LdapImporter.prototype.createSearchAction = function() 
{
    var ldapConfiguration = this.getLdapConfig();
    var dataMappingKeys = Object.keys(this.dataMapping);
    var attributeArray = dataMappingKeys.map(function (key) {
        return new LDAPAttribute(key);
    });
    var action = new SearchAction(ldapConfiguration.login, ldapConfiguration.dn, attributeArray);
    action.setScope(ldapConfiguration.scope);
    action.setFilter(ldapConfiguration.filter);
    action.setLimits(ldapConfiguration.limits);
    action.setPaging(true);
    return action;
};

LdapImporter.prototype.getDataFromDirecory = function() 
{
    var dnArray = this.dn;
    if (typeof(dnArray) != 'object') {
        dnArray = [dnArray]
    }
    
    var urlArray = this.url;
    if (typeof(urlArray) != 'object') {
        dnArray = [urlArray]
    }
    
    for each (var url in urlArray){
        var res = []
        var status = ""
        var message = ""
        
        this.url = url;
        
        for each (var dn in dnArray){
            this.dn = dn;
            
            var searchAction = this.createSearchAction();
            var searchResult = (new LDAPI()).run(searchAction);
            searchResult = new XML(searchResult)
            if (searchResult.status) {
                for each (var entry in searchResult.entrys.entry)
                    res.push(entry);
            }
            else{
                res = [];
                break;
            }
            
            status = "<status>" + searchResult.status + "</status>"
            message = "<message>" + searchResult.message + "</message>"
        
        }
        if (searchResult.status) {
            break;
        }
    }
    var searchXML = "<result><entrys>"
    
    for each (var element in res)
        searchXML += element;
    
    searchXML += "</entrys>" + status + message + "</result>";   
    
    var ldapiResult = new LDAPIResult(searchXML);
    
    if (ldapiResult.getStatus())
        return ldapiResult;
    else
        throw new Error(ldapiResult.getMessage());
};


LdapImporter.prototype.process = function() 
{
    var USERMODEL_PROFILE = tools.PROFILE_FULL;
    var USERMODEL_IDENTIFIER = tools.NAME;//TODO: what happens with PARAMS?
    
    if (this.searchUserInADITO == null)
        throw Error("searchUserInADITO  is not correctly defined");
    
    var usersMissingInLdap = {};//only keep internal userid to ensure a small memory footprint
    //first, collect users that are in ADITO and should be in LDAP
    tools.getStoredUsers().forEach(function (user, idx) 
    {
        if (USERMODEL_IDENTIFIER == tools.NAME)
            usersMissingInLdap[user[0]] = idx;
        else if (USERMODEL_IDENTIFIER == tools.TITLE)
            usersMissingInLdap[user[1]] = idx;
        else
        {
            var loadedUser = tools.getUserByAttribute(tools.NAME, user[0], USERMODEL_PROFILE);
            usersMissingInLdap[loadedUser[USERMODEL_IDENTIFIER]] = idx;
        }
    });
    
    var entries = this.getDataFromDirecory().getEntrys();    
    for(var i = 0, l = entries.length; i < l; i++) 
    {
        var entry = entries[i];
        
        var userModel = this.searchUserInADITO.call(this, USERMODEL_PROFILE, entry);//reminder: if you change the params: remember to adjust the documentation within the constructor
        var preparedEntryData = this._getPreparedData(entry);
        if (userModel == null && this.onUserMissingInADITO != null)
            this.onUserMissingInADITO.call(this, entry, preparedEntryData);//reminder: if you change the params: remember to adjust the documentation within the constructor
        else 
        {
            if (this.onUserModified != null)
                this.onUserModified.call(this, entry, preparedEntryData, userModel);//reminder: if you change the params: remember to adjust the documentation within the constructor                    
            
            delete usersMissingInLdap[userModel[USERMODEL_IDENTIFIER]];
        }
    //attributesMap.clear();
    }
    
    if (this.onUserMissingInLdap != null && usersMissingInLdap)
    {
        Object.keys(usersMissingInLdap).forEach(function (user){
            //since we kept only the reference information (to keep a small memory footprint), we now've to load the dataModel
            //reminder: if you change the params: remember to adjust the documentation within the constructor
            this.onUserMissingInLdap.call(this, tools.getUserByAttribute(USERMODEL_IDENTIFIER, user, USERMODEL_PROFILE));
        }, this);
    }
};

LdapImporter.prototype._getPreparedData = function(pDnEntry)
{
    return this._createAditoKeyValueArray(pDnEntry);
};

/*
 * Erstellt aus einem AD Result Item ein Assoziative Array (Ergebniss Datensatz) mit KEY=Feldname, Value=Wert des AD Attributes.
 * @param pLdapAttribute{AD Result Item}
 * @return {Object} Assoziative Array. Key=FeldName aus der Konfiguration, Value=Wert(z.B des Attributes oder DefaultWert aus der Konfiguration)
 */
LdapImporter.prototype._createAditoKeyValueArray = function(pLdapAttribute)
{
    let ldapAttributes = {};
    
    for (var [key, mapping] in this.dataMapping) 
    {
        if (mapping.FIELD_NAME)
        {
            mapping.FIELD_NAME.forEach(function (fieldName){
                ldapAttributes[fieldName] = this._prepareLdapValue(pLdapAttribute.getAttribute(key), mapping, pLdapAttribute, key, ldapAttributes);
            }, this);
        }
        if (mapping.USER_DATAMODEL)
        {
            mapping.USER_DATAMODEL.forEach(function (fieldName){
                ldapAttributes[fieldName] = this._prepareLdapValue(pLdapAttribute.getAttribute(key), mapping, pLdapAttribute, key, ldapAttributes);
            }, this);
        }
    }
    return ldapAttributes;
};

LdapImporter.prototype._prepareLdapValue = function (pPreparedValue, pMapping, pLdapAttributeObj, pLdapAttributName, pAditoKeyValueArray)
{
    pPreparedValue = (pPreparedValue == null || pPreparedValue.getValues() == null) ? "" : pPreparedValue.getValues().toString();
    
    if(pMapping.FUNCTION) 
    {
        if(typeof pMapping.FUNCTION == "function")
            pPreparedValue = pMapping.FUNCTION(pPreparedValue, pMapping, pLdapAttributeObj, pLdapAttributName, pAditoKeyValueArray);
//        else
//            pPreparedValue = eval(pMapping.FUNCTION);
    } else if(pMapping.MAPPING) {
        pPreparedValue = this._mappingLdapValue(pPreparedValue, pMapping, pLdapAttributeObj, pLdapAttributName, pAditoKeyValueArray);
    }
    return {
        "VALUE": pPreparedValue || pMapping.DEFAULT_VALUE || "",
        "ACTION": pMapping.ACTION || "BOTH"
    };
};

/*
 *@name
 *@desc
 *@param
 *@return
 */
LdapImporter.prototype._mappingLdapValue = function(pPreparedValue, pMapping, pLdapAttributeObj, pLdapAttributName, pAditoKeyValueArray)
{
    let valuesToAssign = [];
    
    for(var key in pMapping.MAPPING) 
    {
        if (pPreparedValue.match(key)) 
        {
            var mapValues = pMapping.MAPPING[key];
            
            if (typeof mapValues == "string") 
            {
                if(mapValues[0] == ";" && mapValues[mapValues.length - 1] == ";") 
                {
                    mapValues = text.decodeMS(mapValues);
                    mapValues = [mapValues];
                }
            }
            
            if(!Array.isArray(mapValues))
                throw "MAPPING for " + pLdapAttributName + " is illegal. It can't be passed to a Array";
            
            for (var value in mapValues) {
                if (valuesToAssign.indexOf(value) == -1 )
                    valuesToAssign.push(value)
            }
        }
    }
    return valuesToAssign;
};

function ActiveDirectoryUtils() {}

ActiveDirectoryUtils.getActiveDirectoryAction = function ()
{
    return {
        BOTH: "BOTH",
        INSERT: "INSERT",
        UPDATE: "UPDATE"
    };
}
ActiveDirectoryUtils._createUpdateUserDatamodel = function (pDataSet, pUserModel)
{
    var LDAP_ACTION = ActiveDirectoryUtils.getActiveDirectoryAction();
    var mode, effectiveUserModel;
    if (pUserModel) 
    {
        mode = LDAP_ACTION.UPDATE;
        effectiveUserModel = pUserModel;
    }
    else
    {
        mode = LDAP_ACTION.INSERT;
        effectiveUserModel = {};
        effectiveUserModel[tools.NAME] = tools.generateNewUserName();
        effectiveUserModel[tools.PARAMS] = [];
        effectiveUserModel[tools.PARAMS][tools.CONTACTID] = pDataSet["CONTACT.CONTACTID"].VALUE;
    }
        
    for each(var mapping in this.dataMapping)
    {
        //falls der Attribut einer Konfiguration nicht für User DataModel konfiguriert, dann überspringen.
        if(!mapping.USER_DATAMODEL || mapping.USER_DATAMODEL.length <= 0)
            continue;
            
        //beim Update dürfen bestimmte Felder nicht aktualisiert werden!
        if(!ActiveDirectoryUtils._isCorrectLdapAction(mapping, mode))
            continue;
            
        for each(var toolsDest in mapping.USER_DATAMODEL)
            eval( "effectiveUserModel" + toolsDest + " = " + JSON.stringify(pDataSet[toolsDest].VALUE));
    }
        
    effectiveUserModel[tools.TITLE] = effectiveUserModel[tools.TITLE].toLowerCase();
    effectiveUserModel[tools.PARAMS][tools.CALENDARID] = effectiveUserModel[tools.PARAMS][tools.CALENDARID].toLowerCase();
    effectiveUserModel[tools.PARAMS][tools.EXCHANGE_EMAIL] = effectiveUserModel[tools.PARAMS][tools.EXCHANGE_EMAIL].toLowerCase();
        
    if(mode == LDAP_ACTION.INSERT)
            logging.log("insert User Model");
            tools.insertUser(effectiveUserModel);
        } 
        catch (onInsertException) 
        { 
            logging.log(effectiveUserModel[tools.NAME])
            logging.log("Error insert User: " + effectiveUserModel[tools.TITLE], logging.ERROR, onInsertException);
            logging.log(JSON.stringify(effectiveUserModel, null, " "), logging.ERROR);
        }
    else                
        tools.updateUser(effectiveUserModel);

}
    
ActiveDirectoryUtils._createInsertUpdateCommMixStatements = function (pDataSet, pContactId)
{
    var LDAP_ACTION = ActiveDirectoryUtils.getActiveDirectoryAction();
    var tempDataset = JSON.parse(JSON.stringify(pDataSet));
    var sqlMixDataSet = [];
    var ldapAction = (pContactId ? LDAP_ACTION.UPDATE : LDAP_ACTION.INSERT);
        
    var existingCommData = null;//only load when necessary, so let's load later
        
    for (var commType in this.custom.commTypes)
        if (pDataSet[commType] == null || !ActiveDirectoryUtils._isCorrectLdapAction(pDataSet[commType], ldapAction))
            continue;
        var commMedium = this.custom.commTypes[commType].MEDIUM;
        if (ldapAction == LDAP_ACTION.INSERT) 
        {
            //if the comm addr is empty nothing else needs to be done (except ignoring that value) - this behaviour must not be same in update-actions
            if (pDataSet[commType].VALUE == "")
                continue;
                
            var temporaryNewDataSet = {};
            ActiveDirectoryUtils._setDataObject(temporaryNewDataSet, "COMMUNICATION.COMMUNICATIONID", util.getNewUUID());
            ActiveDirectoryUtils._setDataObject(temporaryNewDataSet, "COMMUNICATION.MEDIUM_ID", commMedium);
            ActiveDirectoryUtils._setDataObject(temporaryNewDataSet, "COMMUNICATION.ADDR", pDataSet[commType].VALUE);
            ActiveDirectoryUtils._setDataObject(temporaryNewDataSet, "COMMUNICATION.ISSTANDARD", "1");
            ActiveDirectoryUtils._setDataObject(temporaryNewDataSet, "COMMUNICATION.CONTACT_ID", pDataSet["CONTACT.CONTACTID"].VALUE);
                
            ArrayUtils.pushNonEmptyElements(sqlMixDataSet, ActiveDirectoryUtils._createInsertUpdateMixArray.call(this, temporaryNewDataSet, "COMMUNICATION"));
        }
        else
        {
            /*
             * wenn die adresse schon so exisitert: ignorieren
             *
             **/
            if (existingCommData == null)//only load once for a CONTACT
            {
                existingCommData = newSelect("COMMUNICATION.MEDIUM_ID, COMMUNICATION.ISSTANDARD, COMMUNICATION.ADDR")
                .from("COMMUNICATION")
                .where("COMMUNICATION.CONTACT_ID", pContactId)
                .table();
            }

            //wenn comm Wert leer, dann soll der COMM Datensatz im ADITO gelöscht werden.
            if(pDataSet[commType].VALUE == "") //thene DELETE
            {
                sqlMixDataSet.push(["COMMUNICATION", newWhere("COMMUNICATION.CONTACT_ID", pContactId)
                    .and("COMMUNICATION.MEDIUM_ID", commMedium)
                    .buildCondition()]);
                continue;
            }
                
            //UPDATE
            cond = "CONTACT_ID = '" + pContactId + "' and MEDIUM_ID = '" + commMedium + "'";
                
            //INSERT COMM, wenn initial nicht vorliegt.
            var isCommExists = false;
            var isCommEqual = false;
                
            for each(var [MEDIUM_ID, ISSTANDARD, ADDR] in existingCommData) 
            {
                if (MEDIUM_ID == commMedium) 
                {
                    if (ADDR.trim() == pDataSet[commType].VALUE.trim())
                    {
            if (!isCommExists) 
            {
                ActiveDirectoryUtils._setDataObject(pDataSet, "COMMUNICATION.COMMUNICATIONID", util.getNewUUID());
                ActiveDirectoryUtils._setDataObject(pDataSet, "COMMUNICATION.MEDIUM_ID", commMedium);
                ActiveDirectoryUtils._setDataObject(pDataSet, "COMMUNICATION.ADDR", pDataSet[commType].VALUE);
                ActiveDirectoryUtils._setDataObject(pDataSet, "COMMUNICATION.ISSTANDARD", '1');
                ActiveDirectoryUtils._setDataObject(pDataSet, "COMMUNICATION.CONTACT_ID", pContactId);
                    
                let tmpInsUpdC = ActiveDirectoryUtils._createInsertUpdateMixArray.call(this, pDataSet, "COMMUNICATION");
                    
                if (tmpInsUpdC != null) sqlMixDataSet.push(tmpInsUpdC)
            }
            else if (!isCommEqual)
            {
                ActiveDirectoryUtils._setDataObject(pDataSet, "COMMUNICATION.MEDIUM_ID", commMedium);
                ActiveDirectoryUtils._setDataObject(pDataSet, "COMMUNICATION.ADDR", pDataSet[commType].VALUE);
                ActiveDirectoryUtils._setDataObject(pDataSet, "COMMUNICATION.ISSTANDARD", '1');
                ActiveDirectoryUtils._setDataObject(pDataSet, "COMMUNICATION.CONTACT_ID", pContactId);
                let tmpInsUpdC = ActiveDirectoryUtils._createInsertUpdateMixArray.call(this, pDataSet, "COMMUNICATION", cond);
                    
                if (tmpInsUpdC != null) sqlMixDataSet.push(tmpInsUpdC);
            } 
        }
        pDataSet = JSON.parse(JSON.stringify(tempDataset));
    }      
    return (sqlMixDataSet.length == 0) ? null : sqlMixDataSet;
}
    
ActiveDirectoryUtils._initDataSetObject = function(pDataSet, pName)
{
    if(!pDataSet[pName]) 
        pDataSet[pName] = {
            "VALUE": "",
            "ACTION": "BOTH"
        };
}
    
ActiveDirectoryUtils._setDataObject = function (pDataSet, pName, pValue)
{
    ActiveDirectoryUtils._initDataSetObject(pDataSet, pName);
    if (pValue != undefined)
        pDataSet[pName].VALUE = pValue;
}
    
ActiveDirectoryUtils._getColumnsAndTypesArray = function (pTableName, pDataSet, pDbAction)
{
    var columnsTypesArray = {
        "COLUMNS": [],
        "COLUMNS_TYPES": []
    };
        
    var tableConfig = ColumnMetadataProvider.getInstanceFor(pTableName);
    tableConfig.columns.forEach(function (column, idx)
    {
        var dataSet = pDataSet[pTableName + "." + column];
        if(dataSet == null || !ActiveDirectoryUtils._isCorrectLdapAction(dataSet, pDbAction))
            return null;
            
        columnsTypesArray.COLUMNS.push(column);
        columnsTypesArray.COLUMNS_TYPES.push(tableConfig.types[idx]);
        return null;
    });
        
    return columnsTypesArray;
}
    
ActiveDirectoryUtils._isCorrectLdapAction = function (pDataSet, pAction)
{
    var LDAP_ACTION = ActiveDirectoryUtils.getActiveDirectoryAction();
    return (!pDataSet.ACTION || pDataSet.ACTION == LDAP_ACTION.BOTH || pDataSet.ACTION == pAction);
}
    
ActiveDirectoryUtils._getValuesArray = function (pDataSet, pColumnsNames, pTableName)
{
    var valuesArray = [];
    pColumnsNames.forEach(function (columnName){
        if(pDataSet[pTableName + "." + columnName] != null)
            valuesArray.push(pDataSet[pTableName + "." + columnName].VALUE);
    });
    return valuesArray;
}
    
ActiveDirectoryUtils._createInsertUpdateMixArray = function (pDataSet, pTableName, pCondition)
{
    var LDAP_ACTION = ActiveDirectoryUtils.getActiveDirectoryAction();
    var dbAction = (pCondition ? LDAP_ACTION.UPDATE : LDAP_ACTION.INSERT);
    //"this"-reference exists since this helper function will be bound to the importer-module object
    var importUserTitle = this.technicalUserTitel;
    if(dbAction == LDAP_ACTION.INSERT) 
    {
        ActiveDirectoryUtils._setDataObject(pDataSet, pTableName + ".DATE_NEW", datetime.date());
        ActiveDirectoryUtils._setDataObject(pDataSet, pTableName + ".USER_NEW", importUserTitle);
    }
    else
    {
        ActiveDirectoryUtils._setDataObject(pDataSet, pTableName + ".DATE_EDIT", datetime.date());
        ActiveDirectoryUtils._setDataObject(pDataSet, pTableName + ".USER_EDIT", importUserTitle);//this is possible since this helper function will be bound to the importer-module object
    }
        
    var columnsAndTypes = ActiveDirectoryUtils._getColumnsAndTypesArray(pTableName, pDataSet, dbAction);
    var columnNames = columnsAndTypes.COLUMNS;
        
    if(columnNames.length == 0)
        return null;
        
    var columnTypes = columnsAndTypes.COLUMNS_TYPES;
    var values = ActiveDirectoryUtils._getValuesArray(pDataSet, columnNames, pTableName);
    var sqlSatement = [pTableName, columnNames, columnTypes, values];
    if (dbAction == LDAP_ACTION.UPDATE)
        sqlSatement.push(pCondition);
        
    return sqlSatement;
}