<?php

/**
 * class.ldapAdvanced.php
 * LDAP plugin for the RBAC class. This
 */
class LdapAdvanced
{
    /**
     * The authsource id
     * @var String
     */
    public $sAuthSource = "";

    /**
     * The organizational unit where the removed users are put into
     * @var String
     */
    public $sTerminatedOu = "";

    /**
     * a local variable to store connection with LDAP, and avoid multiple bindings
     * @var String
     */
    public $ldapcnn = null;

    /**
     * The users information array
     * @var Array
     */
    public $aUserInfo = array();

    /**
     * System information
     * @var String
     */
    public $sSystem = "";

    /**
     * Object where an rbac instance is set
     * @var Object
     */
    private static $instance = null;

    private $arrayObjectClassFilter = array(
        "user"       => "|(objectclass=inetorgperson)(objectclass=organizationalperson)(objectclass=person)(objectclass=user)",
        "group"      => "|(objectclass=posixgroup)(objectclass=group)(objectclass=groupofuniquenames)",
        "department" => "|(objectclass=organizationalunit)"
    );

    private $arrayAttributes = array(
        "ldap" => array("uid" => "uid",            "member" => "memberuid"),   //OpenLDAP
        "ad"   => array("uid" => "samaccountname", "member" => "member"),      //Active Directory
        "ds"   => array("uid" => "uid",            "member" => "uniquemember") //389 DS
    );

    private $arrayAttributesForUser = array("dn", "uid", "samaccountname", "givenname", "sn", "cn", "mail", "userprincipalname", "useraccountcontrol", "accountexpires", "manager");

    private $frontEnd = false;
    private $debug = false;
    public $arrayAuthenticationSourceUsersByUid = array();
    public $arrayAuthenticationSourceUsersByUsername = array();
    public $arrayDepartmentUsersByUid = array();
    public $arrayDepartmentUsersByUsername = array();
    public $arrayGroupUsersByUid = array();
    public $arrayGroupUsersByUsername = array();

    private $arrayDepartmentUserSynchronizedChecked = array();
    private $arrayUserUpdateChecked = array();

    /**
     * default constructor method
     */
    public function __construct()
    {
    }

    /**
     * Set front end flag
     *
     * @param bool $flag Flag
     *
     * return void
     */
    public function setFrontEnd($flag)
    {
        try {
            $this->frontEnd = $flag;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Set debug
     *
     * @param bool $debug Flag for debug
     *
     * return void
     */
    public function setDebug($debug)
    {
        try {
            $this->debug = $debug;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Set Users that was registered with this Authentication Source
     *
     * @param string $authenticationSourceUid UID of Authentication Source
     *
     * return void
     */
    public function setArrayAuthenticationSourceUsers($authenticationSourceUid)
    {
        try {
            $this->arrayAuthenticationSourceUsersByUid = array();
            $this->arrayAuthenticationSourceUsersByUsername = array();

            //Set data
            $criteria = new Criteria("rbac");

            $criteria->addSelectColumn(RbacUsersPeer::USR_UID);
            $criteria->addSelectColumn(RbacUsersPeer::USR_USERNAME);
            $criteria->addSelectColumn(RbacUsersPeer::USR_AUTH_USER_DN);
            $criteria->add(RbacUsersPeer::UID_AUTH_SOURCE, $authenticationSourceUid, Criteria::EQUAL);
            $criteria->add(RbacUsersPeer::USR_AUTH_TYPE, "ldapadvanced", Criteria::EQUAL);
            //$criteria->add(RbacUsersPeer::USR_STATUS, 1, Criteria::EQUAL);

            $rsCriteria = RbacUsersPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(\ResultSet::FETCHMODE_ASSOC);

            while ($rsCriteria->next()) {
                $row = $rsCriteria->getRow();

                $this->arrayAuthenticationSourceUsersByUid[$row["USR_UID"]] = $row;
                $this->arrayAuthenticationSourceUsersByUsername[$row["USR_USERNAME"]] = $row;
            }
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Set User to this Authentication Source
     *
     * @param string $userUid       UID of User
     * @param array  $arrayUserLdap User LDAP data
     *
     * return void
     */
    public function setArrayAuthenticationSourceUser($userUid, array $arrayUserLdap)
    {
        try {
            $arrayUserData = array(
                "USR_UID"          => $userUid,
                "USR_USERNAME"     => $arrayUserLdap["sUsername"],
                "USR_AUTH_USER_DN" => $arrayUserLdap["sDN"]
            );

            //Set data
            $this->arrayAuthenticationSourceUsersByUid[$arrayUserData["USR_UID"]] = $arrayUserData;
            $this->arrayAuthenticationSourceUsersByUsername[$arrayUserData["USR_USERNAME"]] = $arrayUserData;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Set Users of the Department
     *
     * @param string $departmentUid UID of Department
     *
     * return void
     */
    public function setArrayDepartmentUsers($departmentUid)
    {
        try {
            $this->arrayDepartmentUsersByUid = array();
            $this->arrayDepartmentUsersByUsername = array();

            //Set data
            $criteria = new Criteria("workflow");

            $criteria->addSelectColumn(UsersPeer::USR_UID);
            $criteria->addSelectColumn(UsersPeer::USR_USERNAME);
            $criteria->addSelectColumn(UsersPeer::USR_REPORTS_TO);
            $criteria->add(UsersPeer::DEP_UID, $departmentUid, Criteria::EQUAL);
            $criteria->add(UsersPeer::USR_STATUS, "CLOSED", Criteria::NOT_EQUAL);

            $rsCriteria = UsersPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(\ResultSet::FETCHMODE_ASSOC);

            while ($rsCriteria->next()) {
                $row = $rsCriteria->getRow();

                $this->arrayDepartmentUsersByUid[$row["USR_UID"]] = $row;
                $this->arrayDepartmentUsersByUsername[$row["USR_USERNAME"]] = $row;
            }
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Set Users of the Group
     *
     * @param string $groupUid UID of Group
     *
     * return void
     */
    public function setArrayGroupUsers($groupUid)
    {
        try {
            $this->arrayGroupUsersByUid = array();
            $this->arrayGroupUsersByUsername = array();

            //Set data
            $criteria = new Criteria("workflow");

            $criteria->addSelectColumn(GroupUserPeer::GRP_UID);
            $criteria->addSelectColumn(GroupUserPeer::USR_UID);
            $criteria->addSelectColumn(UsersPeer::USR_USERNAME);
            $criteria->addSelectColumn(UsersPeer::USR_REPORTS_TO);
            $criteria->addJoin(GroupUserPeer::USR_UID, UsersPeer::USR_UID, Criteria::LEFT_JOIN);
            $criteria->add(GroupUserPeer::GRP_UID, $groupUid, Criteria::EQUAL);
            $criteria->add(UsersPeer::USR_STATUS, "CLOSED", Criteria::NOT_EQUAL);

            $rsCriteria = GroupUserPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(\ResultSet::FETCHMODE_ASSOC);

            while ($rsCriteria->next()) {
                $row = $rsCriteria->getRow();

                $this->arrayGroupUsersByUid[$row["USR_UID"]] = $row;
                $this->arrayGroupUsersByUsername[$row["USR_USERNAME"]] = $row;
            }
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Set data to array of Users synchronized (Department)
     *
     * @param array $arrayData Data
     *
     * return void
     */
    public function setArrayDepartmentUserSynchronizedChecked(array $arrayData)
    {
        try {
            $this->arrayDepartmentUserSynchronizedChecked = $arrayData;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Set data to array of updated Users
     *
     * @param array $arrayData Data
     *
     * return void
     */
    public function setArrayUserUpdateChecked(array $arrayData)
    {
        try {
            $this->arrayUserUpdateChecked = $arrayData;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * This method gets the singleton Rbac instance.
     * @return Object instance of the rbac class
     */
    public function &getSingleton()
    {
        if (self::$instance == null) {
            self::$instance = new RBAC();
        }

        return self::$instance;
    }

    /**
     * Progress bar
     *
     * @param int $total Total
     * @param int $count Count
     *
     * return string Return a string that represent progress bar
     */
    public function progressBar($total, $count)
    {
        try {
            $p = (int)(($count * 100) / $total);
            $n = (int)($p / 2);

            return "[" . str_repeat("|", $n) . str_repeat(" ", 50 - $n) . "] $p%";
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Show front end
     *
     * @param string $option Option
     * @param string $data   Data string
     *
     * return void
     */
    public function frontEndShow($option, $data = "")
    {
        try {
            if (!$this->frontEnd) {
                return;
            }

            $numc = 100;

            switch ($option) {
                case "BAR":
                    echo "\r" . "| " . $data . str_repeat(" ", $numc - 2 - strlen($data));
                    break;
                case "TEXT":
                    echo "\r" . "| " . $data . str_repeat(" ", $numc - 2 - strlen($data)) . "\n";
                    break;
                default:
                    //START, END
                    echo "\r" . "+" . str_repeat("-", $numc - 2) . "+" . "\n";
                    break;
            }
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Get valid characteres
     *
     * return array Return an array with valid characteres
     */
    public function characters()
    {
        try {
            $arrayCharacter = array();

            for ($i = 33; $i <= 127; $i++) {
                $char = trim(strtolower(chr($i)));

                if ($char != "") {
                    $arrayCharacter[$i] = $char;
                }
            }

            unset($arrayCharacter[33]);  //!
            unset($arrayCharacter[38]);  //&
            unset($arrayCharacter[40]);  //(
            unset($arrayCharacter[41]);  //)
            unset($arrayCharacter[42]);  //*
            unset($arrayCharacter[60]);  //<
            unset($arrayCharacter[61]);  //=
            unset($arrayCharacter[62]);  //>
            unset($arrayCharacter[124]); //|
            unset($arrayCharacter[126]); //~
            unset($arrayCharacter[127]); //DEL

            //Return
            return array_unique($arrayCharacter);
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Get User data, if Username was registered with this Authentication Source
     *
     * @param string $username Username
     *
     * return array Return User data, if Username was registered with this Authentication Source; empty data otherwise
     */
    public function authenticationSourceGetUserDataIfUsernameExists($username)
    {
        try {
            if (isset($this->arrayAuthenticationSourceUsersByUsername[$username])) {
                return $this->arrayAuthenticationSourceUsersByUsername[$username];
            }

            return array();
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Get User data, if Username exists in Department
     *
     * @param string $username Username
     *
     * return array Return User data, if Username exists in Department; empty data otherwise
     */
    public function departmentGetUserDataIfUsernameExists($username)
    {
        try {
            if (isset($this->arrayDepartmentUsersByUsername[$username])) {
                return $this->arrayDepartmentUsersByUsername[$username];
            }

            return array();
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Get User data, if Username exists in Group
     *
     * @param string $username Username
     *
     * return array Return User data, if Username exists in Group; empty data otherwise
     */
    public function groupGetUserDataIfUsernameExists($username)
    {
        try {
            if (isset($this->arrayGroupUsersByUsername[$username])) {
                return $this->arrayGroupUsersByUsername[$username];
            }

            return array();
        } catch (Exception $e) {
            throw $e;
        }
    }

    public function getFieldsForPageSetup()
    {
        return array();
    }

    /**
     * add a line in the ldap log
     *
     * before the log was generated in shared/sites/<site> folder, but it was deprecated
     * and now we are saving the log in  shared/log the entry in the log file.
     * @author Fernando Ontiveros Lira <fernando@colosa.com>
     * @param Object $_link ldap connection
     * @param String $text
     */
    public function log($link, $text)
    {
        //$serverAddr = $_SERVER["SERVER_ADDR"];
        $logFile = PATH_DATA . "log/ldapAdvanced.log";

        if (!file_exists($logFile) || is_writable($logFile)) {
            $fpt= fopen($logFile, "a");
            $ldapErrorMsg = "";
            $ldapErrorNr = 0;

            if ($link != null) {
                $ldapErrorNr = ldap_errno($link);

                if ($ldapErrorNr != 0) {
                    $ldapErrorMsg = ldap_error($link);
                    $text = $ldapErrorMsg . " : " . $text;
                }
            }

            //log format:   date hour ipaddress workspace ldapErrorNr
            fwrite($fpt, sprintf("%s %s %s %s %s \n", date("Y-m-d H:i:s"), getenv("REMOTE_ADDR"), SYS_SYS, $ldapErrorNr, $text));
            fclose($fpt);
        } else {
            error_log("file $logFile is not writable ");
        }
    }

    /**
     * Add a debug line in the LDAP log
     *
     * @param string $text Text
     *
     * return void
     */
    public function debugLog($text)
    {
        try {
            if ($this->debug) {
                $this->log(null, "DEBUG: $text");
            }
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * This method generates the ldap connection bind and returns the link object
     * for a determined authsource
     * @author Fernando Ontiveros Lira <fernando@colosa.com>
     * @param Array $aAuthSource the authsource data
     * @return Object A object with the resulting ldap bind
     */
    public function ldapConnection($aAuthSource)
    {
        $pass = explode("_", $aAuthSource["AUTH_SOURCE_PASSWORD"]);

        foreach ($pass as $index => $value) {
            if ($value == "2NnV3ujj3w") {
                $aAuthSource["AUTH_SOURCE_PASSWORD"] = G::decrypt($pass[0], $aAuthSource["AUTH_SOURCE_SERVER_NAME"]);
            }
        }

        $ldapcnn = ldap_connect($aAuthSource['AUTH_SOURCE_SERVER_NAME'], $aAuthSource['AUTH_SOURCE_PORT']);

        $ldapServer = $aAuthSource["AUTH_SOURCE_SERVER_NAME"] . ":" . $aAuthSource["AUTH_SOURCE_PORT"] ;

        ldap_set_option($ldapcnn, LDAP_OPT_PROTOCOL_VERSION, 3);
        ldap_set_option($ldapcnn, LDAP_OPT_REFERRALS, 0);

        if (isset($aAuthSource["AUTH_SOURCE_ENABLED_TLS"]) && $aAuthSource["AUTH_SOURCE_ENABLED_TLS"]) {
            @ldap_start_tls($ldapcnn);
            $ldapServer = "TLS " . $ldapServer;
            //$this->log($ldapcnn, "start tls");
        }

        if ($aAuthSource["AUTH_ANONYMOUS"] == "1") {
            $bBind = @ldap_bind($ldapcnn);
            $this->log($ldapcnn, "bind $ldapServer like anonymous user");
        } else {
            $bBind = @ldap_bind($ldapcnn, $aAuthSource['AUTH_SOURCE_SEARCH_USER'], $aAuthSource['AUTH_SOURCE_PASSWORD']);
            $this->log($ldapcnn, "bind $ldapServer with user " . $aAuthSource["AUTH_SOURCE_SEARCH_USER"]);
        }

        if (!$bBind) {
            throw new Exception("Unable to bind to server: $ldapServer . " . "LDAP-Errno: " . ldap_errno($ldapcnn) . " : " . ldap_error($ldapcnn) . " \n");
        }

        return $ldapcnn;
    }

    /**
     * This method obtains the attributes of a ldap Connection passed as parameter
     * @param Object $ldapcnn ldap connection
     * @author Fernando Ontiveros Lira <fernando@colosa.com>
     * @param Object $oEntry Entry object
     * @return Array attributes
     */
    public function ldapGetAttributes($ldapcnn, $entry)
    {
        try {
            $arrayAttributes = array();

            $arrayAttributes['dn'] = ldap_get_dn($ldapcnn, $entry);

            $arrayAux = ldap_get_attributes($ldapcnn, $entry);

            for ($i = 0; $i <= $arrayAux["count"] - 1; $i++) {
                $key = strtolower($arrayAux[$i]);

                switch ($arrayAux[$arrayAux[$i]]["count"]) {
                    case 0:
                        $arrayAttributes[$key] = "";
                        break;
                    case 1:
                        $arrayAttributes[$key] = $arrayAux[$arrayAux[$i]][0];
                        break;
                    default:
                        $arrayAttributes[$key] = $arrayAux[$arrayAux[$i]];

                        unset($arrayAttributes[$key]["count"]);
                        break;
                }
            }

            if (!isset($arrayAttributes["mail"]) && isset($arrayAttributes["userprincipalname"])) {
                $arrayAttributes["mail"] = $arrayAttributes["userprincipalname"];
            }

            return $arrayAttributes;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Get Users from Department (Search result identifier)
     *
     * @param resource $ldapcnn           LDAP link identifier
     * @param resource $searchResult      Search result identifier
     * @param string   $option            Option (GET, SYNCHRONIZE)
     * @param string   $dn                DN
     * @param string   $uidUserIdentifier User identifier
     * @param int      $totalUser         Total users
     * @param int      $countUser         User counter
     * @param array    $arrayData         Data
     *
     * return array Return an array data
     */
    public function ldapGetUsersFromDepartmentSearchResult($ldapcnn, $searchResult, $option, $dn, $uidUserIdentifier, $totalUser, $countUser, array $arrayData)
    {
        try {
            $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartmentSearchResult() > START");

            if ($searchResult) {
                $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartmentSearchResult() > ldap_list > OK");

                $numEntries = ldap_count_entries($ldapcnn, $searchResult);

                $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartmentSearchResult() > ldap_list > OK > \$numEntries ----> $numEntries");

                $totalUser += $numEntries;

                if ($numEntries > 0) {
                    $this->log($ldapcnn, "Search $dn accounts with identifier = $uidUserIdentifier");

                    $entry = ldap_first_entry($ldapcnn, $searchResult);

                    do {
                        $arrayUserLdap = $this->ldapGetAttributes($ldapcnn, $entry);

                        $username = (isset($arrayUserLdap[$uidUserIdentifier]))? $arrayUserLdap[$uidUserIdentifier] : "";

                        $countUser++;

                        if ((is_array($username) && !empty($username)) || trim($username) != "") {
                            $arrayUserData = $this->getUserDataFromAttribute($username, $arrayUserLdap);

                            if (!isset($this->arrayDepartmentUserSynchronizedChecked[$arrayUserData["sUsername"]])) {
                                $this->arrayDepartmentUserSynchronizedChecked[$arrayUserData["sUsername"]] = 1;

                                switch ($option) {
                                    case "GET":
                                        $arrayData[] = $arrayUserData;
                                        break;
                                    case "SYNCHRONIZE":
                                        $arrayData = $this->departmentSynchronizeUser("", $arrayUserData, $arrayData);
                                        break;
                                }
                            } else {
                                $this->log($ldapcnn, "User is repeated: Username \"" . $arrayUserData["sUsername"] . "\", DN \"" . $arrayUserData["sDN"] . "\"");
                            }
                        }

                        if ($option == "SYNCHRONIZE") {
                            //Progress bar
                            $this->frontEndShow("BAR", "Departments: " . $arrayData["i"] . "/" . $arrayData["n"] . " " . $this->progressBar($totalUser, $countUser));
                        }
                    } while ($entry = ldap_next_entry($ldapcnn, $entry));
                }
            }

            $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartmentSearchResult() > END");

            //Return
            return array($totalUser, $countUser, $arrayData);
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Get Users from Department
     *
     * @param string $option    Option (GET, SYNCHRONIZE)
     * @param string $dn        DN of Department
     * @param array  $arrayData Data
     *
     * return array Return an array with data Users or array data
     */
    public function ldapGetUsersFromDepartment($option, $dn, array $arrayData = array())
    {
        try {
            $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartment() > START");
            $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartment() > \$dn ----> $dn");

            $arrayUser = array();
            $totalUser = 0;
            $countUser = 0;

            //Set variables
            $dn = trim($dn);
            $rbac = &RBAC::getSingleton();

            if (is_null($rbac->authSourcesObj)) {
                $rbac->authSourcesObj = new AuthenticationSource();
            }

            $arrayAuthenticationSourceData = $rbac->authSourcesObj->load($this->sAuthSource);

            $this->ldapcnn = $this->ldapConnection($arrayAuthenticationSourceData);

            $ldapcnn = $this->ldapcnn;

            //Get Users
            $limit = $this->__getPageSizeLimitByData($arrayAuthenticationSourceData);
            $flagError = false;

            if (!isset($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_USERS_FILTER"])) {
                $arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_USERS_FILTER"] = "";
            }

            $uidUserIdentifier = (isset($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_IDENTIFIER_FOR_USER"]))? $arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_IDENTIFIER_FOR_USER"] : "uid";

            $filterUsers = trim($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_USERS_FILTER"]);

            $filter = ($filterUsers != "")? $filterUsers : "(" . $this->arrayObjectClassFilter["user"] . ")";

            $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartment() > \$filter ----> $filter");

            $cookie = '';

            do {
                ldap_control_paged_result($ldapcnn, $limit, true, $cookie);

                $searchResult = @ldap_list($ldapcnn, $dn, $filter, $this->arrayAttributesForUser);

                if ($error = ldap_errno($ldapcnn)) {
                    $flagError = true;
                } else {
                    $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartment() > ldap_list > OK");

                    switch ($option) {
                        case "GET":
                            list($totalUser, $countUser, $arrayUser) = $this->ldapGetUsersFromDepartmentSearchResult($ldapcnn, $searchResult, $option, $dn, $uidUserIdentifier, $totalUser, $countUser, $arrayUser);
                            break;
                        case "SYNCHRONIZE":
                            list($totalUser, $countUser, $arrayData) = $this->ldapGetUsersFromDepartmentSearchResult($ldapcnn, $searchResult, $option, $dn, $uidUserIdentifier, $totalUser, $countUser, $arrayData);
                            break;
                    }
                }

                if (!$flagError) {
                    ldap_control_paged_result_response($ldapcnn, $searchResult, $cookie);
                }
            } while (($cookie !== null && $cookie != '') && !$flagError);

            //Get Users //2
            if ($flagError) {
                $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartment() > Search by characters > START");

                foreach ($this->characters() as $value) {
                    $char = $value;

                    $ldapcnn = $this->ldapConnection($arrayAuthenticationSourceData);

                    $filter = ($filterUsers != "")? $filterUsers : "(" . $this->arrayObjectClassFilter["user"] . ")";
                    $filter = "(&$filter($uidUserIdentifier=$char*))";

                    $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartment() > \$filter ----> $filter");

                    $searchResult = @ldap_list($ldapcnn, $dn, $filter, $this->arrayAttributesForUser);

                    if ($error = ldap_errno($ldapcnn)) {
                        $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartment() > ldap_list > ERROR > \$error ---->\n" . print_r($error, true));
                    } else {
                        $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartment() > ldap_list > OK");

                        switch ($option) {
                            case "GET":
                                list($totalUser, $countUser, $arrayUser) = $this->ldapGetUsersFromDepartmentSearchResult($ldapcnn, $searchResult, $option, $dn, $uidUserIdentifier, $totalUser, $countUser, $arrayUser);
                                break;
                            case "SYNCHRONIZE":
                                list($totalUser, $countUser, $arrayData) = $this->ldapGetUsersFromDepartmentSearchResult($ldapcnn, $searchResult, $option, $dn, $uidUserIdentifier, $totalUser, $countUser, $arrayData);
                                break;
                        }
                    }
                }

                $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartment() > Search by characters > END");
            }

            $this->log($ldapcnn, "Found $totalUser users in department $dn");

            $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromDepartment() > END");

            //Return
            switch ($option) {
                case "GET":
                    return $arrayUser;
                    break;
                case "SYNCHRONIZE":
                    return $arrayData;
                    break;
            }
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Synchronize Group's members
     *
     * @param resource $ldapcnn             LDAP link identifier
     * @param array    $arrayAuthSourceData Authentication Source Data
     * @param string   $groupUid            Unique id of Group
     * @param array    $arrayGroupLdap      LDAP Group
     * @param string   $memberAttribute     Member attribute
     * @param array    $arrayData           Data
     *
     * @return array Return array data
     */
    private function __ldapGroupSynchronizeMembers(
        $ldapcnn,
        array $arrayAuthSourceData,
        $groupUid,
        array $arrayGroupLdap,
        $memberAttribute,
        array $arrayData = []
    ) {
        try {
            unset($arrayData['countMembers']);

            //Get members
            if (!isset($arrayAuthSourceData['AUTH_SOURCE_DATA']['AUTH_SOURCE_USERS_FILTER'])) {
                $arrayAuthSourceData['AUTH_SOURCE_DATA']['AUTH_SOURCE_USERS_FILTER'] = '';
            }

            $uidUserIdentifier = (isset($arrayAuthSourceData['AUTH_SOURCE_DATA']['AUTH_SOURCE_IDENTIFIER_FOR_USER']))?
                $arrayAuthSourceData['AUTH_SOURCE_DATA']['AUTH_SOURCE_IDENTIFIER_FOR_USER'] : 'uid';

            $filterUsers = trim($arrayAuthSourceData['AUTH_SOURCE_DATA']['AUTH_SOURCE_USERS_FILTER']);

            $filter = ($filterUsers != '')? $filterUsers : '(' . $this->arrayObjectClassFilter['user'] . ')';

            if (isset($arrayGroupLdap[$memberAttribute])) {
                if (!is_array($arrayGroupLdap[$memberAttribute])) {
                    $arrayGroupLdap[$memberAttribute] = [$arrayGroupLdap[$memberAttribute]];
                }

                $arrayData['countMembers'] = count($arrayGroupLdap[$memberAttribute]);
                $arrayData['totalUser'] += $arrayData['countMembers'];

                //Synchronize members
                foreach ($arrayGroupLdap[$memberAttribute] as $value) {
                    $member = $value; //User DN

                    $searchResult = @ldap_search($ldapcnn, $member, $filter, $this->arrayAttributesForUser);

                    if ($error = ldap_errno($ldapcnn)) {
                        //
                    } else {
                        if ($searchResult) {
                            if (ldap_count_entries($ldapcnn, $searchResult) > 0) {
                                $entry = ldap_first_entry($ldapcnn, $searchResult);

                                $arrayUserLdap = $this->ldapGetAttributes($ldapcnn, $entry);

                                $username = (isset($arrayUserLdap[$uidUserIdentifier]))? $arrayUserLdap[$uidUserIdentifier] : '';

                                $arrayData['countUser']++;

                                if ((is_array($username) && !empty($username)) || trim($username) != '') {
                                    $arrayData = $this->groupSynchronizeUser(
                                        $groupUid, $this->getUserDataFromAttribute($username, $arrayUserLdap), $arrayData
                                    );
                                }

                                //Progress bar
                                $this->frontEndShow(
                                    'BAR',
                                    'Groups: ' . $arrayData['i'] . '/' . $arrayData['n'] . ' ' .
                                    $this->progressBar($arrayData['totalUser'], $arrayData['countUser'])
                                );
                            }
                        }
                    }
                }
            }

            //Return
            return $arrayData;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Get Users from Group
     *
     * @param string $option         Option (SYNCHRONIZE)
     * @param array  $arrayGroupData Group data
     * @param array  $arrayData      Data
     *
     * return array Return array data
     */
    public function ldapGetUsersFromGroup($option, array $arrayGroupData, array $arrayData = array())
    {
        try {
            $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromGroup() > START");
            $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromGroup() > \$arrayGroupData ---->\n" . print_r($arrayGroupData, true));

            $totalUser = 0;
            $countUser = 0;

            //Set variables
            $dn = trim($arrayGroupData["GRP_LDAP_DN"]);
            $rbac = &RBAC::getSingleton();

            if (is_null($rbac->authSourcesObj)) {
                $rbac->authSourcesObj = new AuthenticationSource();
            }

            $arrayAuthenticationSourceData = $rbac->authSourcesObj->load($this->sAuthSource);

            $this->ldapcnn = $this->ldapConnection($arrayAuthenticationSourceData);

            $ldapcnn = $this->ldapcnn;

            //Get Group members
            $memberAttribute = $this->arrayAttributes[$arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["LDAP_TYPE"]]["member"];

            $filter = "(" . $this->arrayObjectClassFilter["group"] . ")";

            $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromGroup() > \$filter ----> $filter");

            $searchResult = @ldap_search($ldapcnn, $dn, $filter, array($memberAttribute));

            if ($error = ldap_errno($ldapcnn)) {
                $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromGroup() > ldap_search > ERROR > \$error ---->\n" . print_r($error, true));
            } else {
                $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromGroup() > ldap_search > OK1");

                if ($searchResult) {
                    $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromGroup() > ldap_search > OK2");

                    $numEntries = ldap_count_entries($ldapcnn, $searchResult);

                    $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromGroup() > ldap_search > OK2 > \$numEntries ----> $numEntries");

                    if ($numEntries > 0) {
                        $entry = ldap_first_entry($ldapcnn, $searchResult);

                        $arrayGroupLdap = $this->ldapGetAttributes($ldapcnn, $entry);

                        //Syncronize members
                        $flagMemberRange = false;

                        $memberAttribute2 = $memberAttribute;

                        if (isset($arrayGroupLdap[$memberAttribute]) && empty($arrayGroupLdap[$memberAttribute])) {
                            foreach ($arrayGroupLdap as $key => $value) {
                                if (preg_match('/^member;range=\d+\-\d+$/i', $key)) {
                                    $memberAttribute2 = $key;

                                    $flagMemberRange = true;
                                    break;
                                }
                            }
                        }

                        $arrayData = $this->__ldapGroupSynchronizeMembers(
                            $ldapcnn,
                            $arrayAuthenticationSourceData,
                            $arrayGroupData['GRP_UID'],
                            $arrayGroupLdap,
                            $memberAttribute2,
                            array_merge($arrayData, ['totalUser' => $totalUser, 'countUser' => $countUser])
                        );

                        $totalUser = $arrayData['totalUser'];
                        $countUser = $arrayData['countUser'];

                        $limitMemberRange = (isset($arrayData['countMembers']))? $arrayData['countMembers'] : 0;

                        if ($flagMemberRange) {
                            for ($start = $limitMemberRange; true; $start += $limitMemberRange) {
                                $end = $start + $limitMemberRange - 1;

                                $memberAttribute2 = $memberAttribute . ';range=' . $start . '-' . $end;

                                $searchResult = @ldap_search($ldapcnn, $dn, $filter, [$memberAttribute2]);

                                if ($error = ldap_errno($ldapcnn)) {
                                    break;
                                } else {
                                    if ($searchResult) {
                                        if (ldap_count_entries($ldapcnn, $searchResult) > 0) {
                                            $entry = ldap_first_entry($ldapcnn, $searchResult);

                                            $arrayGroupLdap = $this->ldapGetAttributes($ldapcnn, $entry);

                                            foreach ($arrayGroupLdap as $key => $value) {
                                                if (preg_match('/^member;range=\d+\-\*$/i', $key)) {
                                                    $memberAttribute2 = $key;
                                                    break;
                                                }
                                            }

                                            $arrayData = $this->__ldapGroupSynchronizeMembers(
                                                $ldapcnn,
                                                $arrayAuthenticationSourceData,
                                                $arrayGroupData['GRP_UID'],
                                                $arrayGroupLdap,
                                                $memberAttribute2,
                                                array_merge($arrayData, ['totalUser' => $totalUser, 'countUser' => $countUser])
                                            );

                                            $totalUser = $arrayData['totalUser'];
                                            $countUser = $arrayData['countUser'];
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            $this->log($ldapcnn, "Found $totalUser users in group $dn");

            $this->debugLog("class.ldapAdvanced.php > function ldapGetUsersFromGroup() > END");

            //Return
            return $arrayData;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * This method authentifies if a user has the RBAC_user privileges
     * also verifies if the user has the rights to start an application
     *
     * @author Fernando Ontiveros Lira <fernando@colosa.com>
     * @access public

     * @param  string $strUser    UserId  (user login)
     * @param  string $strPass    Password
     * @return
     *  -1: user doesn"t exists / no existe usuario
     *  -2: wrong password / password errado
     *  -3: inactive user / usuario inactivo
     *  -4: user due date / usuario vencido
     *  -5: connection error
     *  n : user uid / uid de usuario
     */
    public function VerifyLogin($strUser, $strPass)
    {
        if (is_array($strUser)) {
            $strUser     = $strUser[0];
        } else {
            $strUser     = trim($strUser);
        }

        if ($strUser == "") {
            return -1;
        }

        if (strlen($strPass) == 0) {
            return -2;
        }

        $ldapcnn = null;

        $validUserPass = 1;

        try {
            $rbac = &RBAC::getSingleton();

            if (is_null($rbac->authSourcesObj)) {
                $rbac->authSourcesObj = new AuthenticationSource();
            }

            if ($rbac->userObj == null) {
                $rbac->userObj = new RbacUsers();
            }

            $arrayAuthSource = $rbac->authSourcesObj->load($this->sAuthSource);

            $setAttributes = 0;

            if (isset($arrayAuthSource['AUTH_SOURCE_DATA']['AUTH_SOURCE_SHOWGRID']) &&
                $arrayAuthSource['AUTH_SOURCE_DATA']['AUTH_SOURCE_SHOWGRID'] == 'on'
            ) {
                $setAttributes = 1;
            }

            //Get UserName
            $criteria = new Criteria("rbac");

            $criteria->addSelectColumn(RbacUsersPeer::USR_USERNAME);
            $criteria->addSelectColumn(RbacUsersPeer::USR_UID);
            $criteria->add(RbacUsersPeer::UID_AUTH_SOURCE, $arrayAuthSource["AUTH_SOURCE_UID"]);
            $criteria->add(RbacUsersPeer::USR_AUTH_USER_DN, $strUser);
            $criteria->add(RbacUsersPeer::USR_USERNAME, "", Criteria::NOT_EQUAL);

            $rsCriteria = RbacUsersPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(ResultSet::FETCHMODE_ASSOC);

            $rsCriteria->next();
            $row = $rsCriteria->getRow();

            $usrName = $row["USR_USERNAME"];
            $usrUid = $row["USR_UID"];

            //Get the AuthSource properties
            //Check if the dn in the database record matches with the dn for the ldap account
            $verifiedUser = $this->searchUserByUid(
                $usrName,
                $arrayAuthSource["AUTH_SOURCE_DATA"]["AUTH_SOURCE_IDENTIFIER_FOR_USER"]
            );

            if (empty($verifiedUser) || trim($verifiedUser["sDN"]) == null) {
                return -1;
            }

            $userDn = $strUser;

            if ($verifiedUser["sDN"] != $strUser || $setAttributes==1) {
                $userDn = $verifiedUser['sDN'];

                //Update data
                $user = new \ProcessMaker\BusinessModel\User();
                $arrayUserData = $user->getUserRecordByPk($usrUid, [], false);

                $result = $this->__ldapUserUpdateByDnAndData(
                    $this->ldapcnn, $arrayAuthSource, $userDn, [$arrayUserData['USR_USERNAME'] => $arrayUserData]
                );

                //Update DN
                $con = Propel::getConnection(RbacUsersPeer::DATABASE_NAME);
                // select set
                $c1 = new Criteria("rbac");
                $c1->add(RbacUsersPeer::UID_AUTH_SOURCE, $arrayAuthSource["AUTH_SOURCE_UID"]);
                $c1->add(RbacUsersPeer::USR_AUTH_USER_DN, $strUser);
                // update set
                $c2 = new Criteria("rbac");
                $c2->add(RbacUsersPeer::USR_AUTH_USER_DN, $userDn);

                BasePeer::doUpdate($c1, $c2, $con);
            }

            //Check ldap connection for user
            $arrayAuthSource["AUTH_ANONYMOUS"]          = "0";
            $arrayAuthSource["AUTH_SOURCE_SEARCH_USER"] = $userDn;
            $arrayAuthSource["AUTH_SOURCE_PASSWORD"]    = $strPass;

            $ldapcnn = $this->ldapConnection($arrayAuthSource);
            $flagUpdate = false;
            switch (ldap_errno($ldapcnn)) {
                case '0x00':
                    $flagUpdate = true;
                    $statusRbac = 1;
                    $statusUser = 'ACTIVE';
                    break;
                case '0x34':
                case '0x58':
                case '0x5e':
                    //LDAP_UNAVAILABLE
                    //LDAP_USER_CANCELLED
                    //LDAP_NO_RESULTS_RETURNED
                    $flagUpdate = true;
                    $statusRbac = 0;
                    $statusUser = 'INACTIVE';
                    break;
                default:
                    break;
            }
            if ($flagUpdate) {
                $con = Propel::getConnection(RbacUsersPeer::DATABASE_NAME);
                // select set
                $c1 = new Criteria("rbac");
                $c1->add(RbacUsersPeer::UID_AUTH_SOURCE, $arrayAuthSource["AUTH_SOURCE_UID"]);
                $c1->add(RbacUsersPeer::USR_AUTH_USER_DN, $strUser);
                // update set
                $c2 = new Criteria("rbac");
                $c2->add(RbacUsersPeer::USR_AUTH_USER_DN, $verifiedUser["sDN"]);
                $c2->add(RbacUsersPeer::USR_STATUS, $statusRbac);
                BasePeer::doUpdate($c1, $c2, $con);
                $columnsWf = array();
                $columnsWf['USR_UID'] = $usrUid;
                $columnsWf['USR_STATUS'] = $statusUser;
                $oUser = new Users();
                $oUser->update($columnsWf);
            }

            $attributes = $arrayAuthSource["AUTH_SOURCE_DATA"];

            if (!isset($attributes['AUTH_SOURCE_RETIRED_OU'])) {
                $attributes ['AUTH_SOURCE_RETIRED_OU'] = '';
            }

            //Check if the user is in the terminated organizational unit
            if ($this->userIsTerminated($usrName, $attributes["AUTH_SOURCE_RETIRED_OU"])) {
                $this->deactivateUser($usrName);
                $this->log($ldapcnn, "user $strUser is member of Remove OU, deactivating this user.");

                return -3;
            }
            $validUserPass = ldap_errno($ldapcnn) == 0;
        } catch (Exception $e) {
            $validUserPass = -5;
        }

        if ($validUserPass == 1) {
            $this->log($ldapcnn, "sucessful login user " . $verifiedUser["sDN"]);
        } else {
            $this->log($ldapcnn, "failure authentication for user $strUser");
        }

        return $validUserPass ;
    }

    /**
     * Get data of a User from attribute
     *
     * @param mixed $username        Username
     * @param array $arrayAttributes Attributes
     *
     * return array Return an array with data User
     */
    public function getUserDataFromAttribute($username, array $arrayAttributes)
    {
        try {
            $keyMail = (isset($arrayAttributes["mail"]))? "mail" : ((isset($arrayAttributes["userprincipalname"]))? "userprincipalname" : "nomail");

            return array(
                "sUsername"  => trim((is_array($username))? $username[0] : $username),
                "sPassword"  => trim((isset($arrayAttributes["userpassword"]))? ((is_array($arrayAttributes["userpassword"]))? $arrayAttributes["userpassword"][0] : $arrayAttributes["userpassword"]) : ""),
                "sFullname"  => trim((isset($arrayAttributes["cn"]))?           ((is_array($arrayAttributes["cn"]))?           $arrayAttributes["cn"][0] : $arrayAttributes["cn"]) : ""),
                "sFirstname" => trim((isset($arrayAttributes["givenname"]))?    ((is_array($arrayAttributes["givenname"]))?    $arrayAttributes["givenname"][0] : $arrayAttributes["givenname"]) : ""),
                "sLastname"  => trim((isset($arrayAttributes["sn"]))?           ((is_array($arrayAttributes["sn"]))?           $arrayAttributes["sn"][0] : $arrayAttributes["sn"]) : ""),
                "sEmail"     => trim((isset($arrayAttributes[$keyMail]))?       ((is_array($arrayAttributes[$keyMail]))?       $arrayAttributes[$keyMail][0] : $arrayAttributes[$keyMail]) : ""),
                "sDN"        => trim($arrayAttributes["dn"]),
                "sManagerDN" => trim((isset($arrayAttributes["manager"]))?      ((is_array($arrayAttributes["manager"]))?      $arrayAttributes["manager"][0] : $arrayAttributes["manager"]) : "")
            );
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * This method searches for the users that has some attribute
     * that matches the keyword.
     * @param String $keyword search criteria
     * @return array Users that match the search criteria
     */
    public function searchUsers($keyword, $start = null, $limit = null)
    {
        $arrayUser = array();
        $totalUser = 0;
        $countUser = 0;

        $keyword = trim(trim($keyword), "*");
        $keyword = ($keyword != "")? "*$keyword*" : "*";

        $paged = !is_null($start) && !is_null($limit);

        $rbac = &RBAC::getSingleton();

        if (is_null($rbac->authSourcesObj)) {
            $rbac->authSourcesObj = new AuthenticationSource();
        }

        $arrayAuthenticationSourceData = $rbac->authSourcesObj->load($this->sAuthSource);
        $attributeUserSet = array();
        $attributeSetAdd = array();

        if (isset($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_GRID_ATTRIBUTE"])
            && !empty($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_GRID_ATTRIBUTE"])
        ) {
            foreach ($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_GRID_ATTRIBUTE"] as $value) {
                $attributeSetAdd[] = $value['attributeLdap'];
                $attributeUserSet[$value['attributeUser']] = $value['attributeLdap'];
            }
        }

        if (is_null($this->ldapcnn)) {
            $this->ldapcnn = $this->ldapConnection($arrayAuthenticationSourceData);
        }

        $ldapcnn = $this->ldapcnn;

        //Get Users
        if (!isset($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_USERS_FILTER"])) {
            $arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_USERS_FILTER"] = "";
        }

        $uidUserIdentifier = (isset($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_IDENTIFIER_FOR_USER"]))? $arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_IDENTIFIER_FOR_USER"] : "uid";

        $filterUsers = trim($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_USERS_FILTER"]);

        $filter = ($filterUsers != "")? $filterUsers : "(" . $this->arrayObjectClassFilter["user"] . ")";
        $filter = "(&$filter(|(dn=$keyword)(uid=$keyword)(samaccountname=$keyword)(givenname=$keyword)(sn=$keyword)(cn=$keyword)(mail=$keyword)(userprincipalname=$keyword)))";

        $oSearch = @ldap_search($ldapcnn, $arrayAuthenticationSourceData["AUTH_SOURCE_BASE_DN"], $filter, array_merge($this->arrayAttributesForUser, $attributeSetAdd));

        if ($oError = ldap_errno($ldapcnn)) {
            $this->log($ldapcnn, "Error in Search users");
        } else {
            if ($oSearch) {
                $entries = ldap_count_entries($ldapcnn, $oSearch);
                $totalUser = $entries;

                if ($entries > 0) {
                    $oEntry = ldap_first_entry($ldapcnn, $oSearch);

                    $countEntries=0;

                    $flagNextRecord = true;

                    do {
                        $aAttr = $this->ldapGetAttributes($ldapcnn, $oEntry);
                        $sUsername = (isset($aAttr[$uidUserIdentifier]))? $aAttr[$uidUserIdentifier] : "";

                        if ((is_array($sUsername) && !empty($sUsername)) || trim($sUsername) != "") {
                            $countUser++;

                            /*Active Directory userAccountControl Values
                            Normal Day to Day Values:
                            512 - Enable Account
                            514 - Disable account
                            544 - Account Enabled - Require user to change password at first logon
                            4096 - Workstation/server
                            66048 - Enabled, password never expires
                            66050 - Disabled, password never expires
                            262656 - Smart Card Logon Required
                            532480 - Domain controller
                            1 - script
                            2 - accountdisable
                            8 - homedir_required
                            16 - lockout
                            32 - passwd_notreqd
                            64 - passwd_cant_change
                            128 - encrypted_text_pwd_allowed
                            256 - temp_duplicate_account
                            512 - normal_account
                            2048 - interdomain_trust_account
                            4096 - workstation_trust_account
                            8192 - server_trust_account
                            65536 - dont_expire_password
                            131072 - mns_logon_account
                            262144 - smartcard_required
                            524288 - trusted_for_delegation
                            1048576 - not_delegated
                            2097152 - use_des_key_only
                            4194304 - dont_req_preauth
                            8388608 - password_expired
                            16777216 - trusted_to_auth_for_delegation
                            */
                            $userCountControl = '';
                            //Active Directory, openLdap
                            if (isset($aAttr['useraccountcontrol'])) {
                                switch ($aAttr['useraccountcontrol']) {
                                    case '512':
                                    case '544':
                                    case '66048':
                                    case '66080':
                                        $userCountControl = 'ACTIVE';
                                        break;
                                    case '514':
                                    case '546':
                                    case '66050':
                                    case '66082':
                                    case '2':
                                    case '16':
                                    case '8388608':
                                    default:
                                        $userCountControl = 'INACTIVE';
                                        break;
                                }
                            }
                            //apache ldap
                            if (isset($aAttr['status'])) {
                                $userCountControl = strtoupper($aAttr['status']);
                            }
                            $aUserAttributes = array();
                            foreach ($attributeUserSet as $key => $value) {
                                if ($key == 'USR_STATUS') {
                                    $aUserAttributes[$key] = ($userCountControl != '') ? $userCountControl : 'ACTIVE';
                                } elseif (isset($aAttr[ $value ])) {
                                    $aUserAttributes[$key] = $aAttr[ $value ];
                                }
                            }

                            if ($paged) {
                                if ($countUser - 1 <= $start + $limit - 1) {
                                    if ($start <= $countUser - 1) {
                                        $arrayUser[] = array_merge($this->getUserDataFromAttribute($sUsername, $aAttr), $aUserAttributes);
                                    }
                                } else {
                                    $flagNextRecord = false;
                                }
                            } else {
                                $arrayUser[] = array_merge($this->getUserDataFromAttribute($sUsername, $aAttr), $aUserAttributes);
                            }

                            $countEntries++;
                        }
                    } while (($oEntry = ldap_next_entry($ldapcnn, $oEntry)) && $flagNextRecord);
                }
            }
            /*
            $sUsers = "found $countEntries users: ";

            foreach ($aUsers as $key => $val) {
                $sUsers .= $val['sUsername'] . ' ';
            }

            $this->log($ldapcnn, $sUsers);
            */
        }

        //Return
        return ($paged)? array("numRecTotal" => $totalUser, "data" => $arrayUser) : $arrayUser;
    }

    /**
     * This method search in the ldap/active directory source for an user using the UID, (samaccountname or uid )
     * the value should be in $aAuthSource['AUTH_SOURCE_DATA']['AUTH_SOURCE_IDENTIFIER_FOR_USER']
     * @param String $keyword The keyword in order to match the record with the identifier attribute
     * @param String $identifier id identifier, this parameter is optional
     * @return mixed if the user has been found or not
     */
    public function searchUserByUid($keyword, $identifier = "")
    {
        try {
            $arrayUserData = array();

            //Set variables
            $rbac = &RBAC::getSingleton();
            //$rbac->userObj = new RbacUsers();

            if (is_null($rbac->authSourcesObj)) {
                $rbac->authSourcesObj = new AuthenticationSource();
            }

            $arrayAuthenticationSourceData = $rbac->authSourcesObj->load($this->sAuthSource);

            if (is_null($this->ldapcnn)) {
                $this->ldapcnn = $this->ldapConnection($arrayAuthenticationSourceData);
            }

            $ldapcnn = $this->ldapcnn;

            //Get User
            $attributeUserSet = array();
            $attributeSetAdd = array();

            if (isset($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_GRID_ATTRIBUTE"])
                && !empty($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_GRID_ATTRIBUTE"])
            ) {
                foreach ($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_GRID_ATTRIBUTE"] as $value) {
                    $attributeSetAdd[] = $value["attributeLdap"];
                    $attributeUserSet[$value["attributeUser"]] = $value["attributeLdap"];
                }
            }

            $uidUserIdentifier = (isset($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_IDENTIFIER_FOR_USER"]))? $arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_IDENTIFIER_FOR_USER"] : "uid";

            $filter2 = "";

            if ($identifier != "" && $identifier != $uidUserIdentifier) {
                $filter2 = "($identifier=$keyword)";
            }

            $filter = "(&(" . $this->arrayObjectClassFilter["user"] . ")(|($uidUserIdentifier=$keyword)$filter2))";

            $searchResult = @ldap_search($ldapcnn, $arrayAuthenticationSourceData["AUTH_SOURCE_BASE_DN"], $filter, array_merge($this->arrayAttributesForUser, $attributeSetAdd));

            if ($error = ldap_errno($ldapcnn)) {
                //
            } else {
                if ($searchResult) {
                    $numEntries = ldap_count_entries($ldapcnn, $searchResult);

                    if ($numEntries > 0) {
                        $entry = ldap_first_entry($ldapcnn, $searchResult);

                        $arrayUserLdap = $this->ldapGetAttributes($ldapcnn, $entry);

                        $username = (isset($arrayUserLdap[$uidUserIdentifier]))? $arrayUserLdap[$uidUserIdentifier] : "";

                        if ((is_array($username) && !empty($username)) || trim($username) != "") {
                            $userCountControl = "";

                            //Active Directory, OpenLDAP
                            if (isset($arrayUserLdap["useraccountcontrol"])) {
                                switch ($arrayUserLdap["useraccountcontrol"]) {
                                    case "512":
                                    case "544":
                                    case "66048":
                                    case "66080":
                                        $userCountControl = "ACTIVE";
                                        break;
                                    case "514":
                                    case "546":
                                    case "66050":
                                    case "66082":
                                    case "2":
                                    case "16":
                                    case "8388608":
                                    default:
                                        $userCountControl = "INACTIVE";
                                        break;
                                }
                            }

                            //Apache LDAP
                            if (isset($arrayUserLdap["status"])) {
                                $userCountControl = strtoupper($arrayUserLdap["status"]);
                            }

                            $aUserAttributes = array();

                            foreach ($attributeUserSet as $key => $value) {
                                if ($key == "USR_STATUS") {
                                    $aUserAttributes[$key] = ($userCountControl != "")? $userCountControl : "ACTIVE";
                                } else {
                                    if (isset($arrayUserLdap[$value])) {
                                        $aUserAttributes[$key] = $arrayUserLdap[$value];
                                    }
                                }
                            }

                            $arrayUserData = array_merge($this->getUserDataFromAttribute($username, $arrayUserLdap), $aUserAttributes);
                        }
                    }
                }
            }

            //Return
            return $arrayUserData;
        } catch (Exception $e) {
            throw $e;
        }
    }

    public function automaticRegister($aAuthSource, $strUser, $strPass)
    {
        $rbac = &RBAC::getSingleton();

        if ($rbac->userObj == null) {
            $rbac->userObj = new RbacUsers();
        }

        if ($rbac->rolesObj == null) {
            $rbac->rolesObj = new Roles();
        }

        $user = $this->searchUserByUid($strUser);

        $res = 0;

        if (!empty($user)) {
            if ($this->VerifyLogin($user['sUsername'], $strPass) === true) {
                $res = 1;
            }

            if ($res == 0 &&  $this->VerifyLogin($user['sDN'], $strPass) === true) {
                $res = 1;
            }
        } else {
            return $res;
        }

        if ($res == 0) {
            $aAuthSource = $rbac->authSourcesObj->load($this->sAuthSource);
            $aAttributes = array();

            if (isset($aAuthSource['AUTH_SOURCE_DATA']['AUTH_SOURCE_GRID_ATTRIBUTE'])) {
                $aAttributes = $aAuthSource['AUTH_SOURCE_DATA']['AUTH_SOURCE_GRID_ATTRIBUTE'];
            }

            $aData = array();
            $aData['USR_USERNAME']     = $user['sUsername'];
            $aData["USR_PASSWORD"]     = "00000000000000000000000000000000";
            $aData['USR_FIRSTNAME']    = $user['sFirstname'];
            $aData['USR_LASTNAME']     = $user['sLastname'];
            $aData['USR_EMAIL']        = $user['sEmail'];
            $aData['USR_DUE_DATE']     = date('Y-m-d', mktime(0, 0, 0, date('m'), date('d'), date('Y') + 2));
            $aData['USR_CREATE_DATE']  = date('Y-m-d H:i:s');
            $aData['USR_UPDATE_DATE']  = date('Y-m-d H:i:s');
            $aData['USR_BIRTHDAY']     = date('Y-m-d');
            $aData['USR_STATUS']       = (isset($user['USR_STATUS'])) ? (($user['USR_STATUS'] == 'ACTIVE') ? 1 : 0) : 1;
            $aData['USR_AUTH_TYPE']    = strtolower($aAuthSource['AUTH_SOURCE_PROVIDER']);
            $aData['UID_AUTH_SOURCE']  = $aAuthSource['AUTH_SOURCE_UID'];
            $aData['USR_AUTH_USER_DN'] = $user['sDN'];
            $aData['USR_ROLE']         = 'PROCESSMAKER_OPERATOR';

            if (!empty($aAttributes)) {
                foreach ($aAttributes as $value) {
                    if (isset($user[$value['attributeUser']])) {
                        $aData[$value['attributeUser']] = str_replace("*", "'", $user[$value['attributeUser']]);
                        if ($value['attributeUser'] == 'USR_STATUS') {
                            $evalValue = $aData[$value['attributeUser']];
                            $statusValue = (isset($user['USR_STATUS'])) ? $user['USR_STATUS'] :'ACTIVE';
                            $aData[$value['attributeUser']] = $statusValue;
                        }
                    }
                }
            }

            //req - accountexpires
            if (isset($user["USR_DUE_DATE"]) && $user["USR_DUE_DATE"]!='') {
                $aData["USR_DUE_DATE"] = $this->convertDateADtoPM($user["USR_DUE_DATE"]);
            }
            //end

            $sUserUID = $rbac->createUser($aData, 'PROCESSMAKER_OPERATOR');
            $aData['USR_UID'] = $sUserUID;

            require_once 'classes/model/Users.php';

            $oUser = new Users();
            $aData['USR_STATUS']       = (isset($user['USR_STATUS'])) ? $user['USR_STATUS'] : 'ACTIVE';
            $oUser->create($aData);
            $this->log(null, "Automatic Register for user $strUser ");
            $res = 1;
        }

        return $res;
    }

    /**
     * Get a deparment list
     * @return <type>
     */
    public function searchDepartments()
    {
        try {
            $arrayDepartment = [];

            //Set variables
            $rbac = &RBAC::getSingleton();

            if (is_null($rbac->authSourcesObj)) {
                $rbac->authSourcesObj = new AuthenticationSource();
            }

            $arrayAuthenticationSourceData = $rbac->authSourcesObj->load($this->sAuthSource);

            if (is_null($this->ldapcnn)) {
                $this->ldapcnn = $this->ldapConnection($arrayAuthenticationSourceData);
            }

            $ldapcnn = $this->ldapcnn;

            //Get Departments
            $limit = $this->__getPageSizeLimitByData($arrayAuthenticationSourceData);
            $flagError = false;

            $filter = '(' . $this->arrayObjectClassFilter['department'] . ')';

            $this->log($ldapcnn, 'search Departments with Filter: ' . $filter);

            $unitsBase = $this->custom_ldap_explode_dn($arrayAuthenticationSourceData['AUTH_SOURCE_BASE_DN']);

            $cookie = '';

            do {
                ldap_control_paged_result($ldapcnn, $limit, true, $cookie);

                $searchResult = @ldap_search($ldapcnn, $arrayAuthenticationSourceData['AUTH_SOURCE_BASE_DN'], $filter, ['dn', 'ou']);

                if ($error = ldap_errno($ldapcnn)) {
                    $this->log($ldapcnn, 'Error in Search');

                    $flagError = true;
                } else {
                    if ($searchResult) {
                        //The first node is root
                        if (empty($arrayDepartment)) {
                            $arrayDepartment[] = [
                                'dn'     => $arrayAuthenticationSourceData['AUTH_SOURCE_BASE_DN'],
                                'parent' => '',
                                'ou'     => 'ROOT',
                                'users'  => 0
                            ];
                        }

                        //Get departments from the ldap entries
                        if (ldap_count_entries($ldapcnn, $searchResult) > 0) {
                            $entry = ldap_first_entry($ldapcnn, $searchResult);

                            do {
                                $arrayEntryData = $this->ldapGetAttributes($ldapcnn, $entry);
                                $unitsEqual = $this->custom_ldap_explode_dn($arrayEntryData['dn']);

                                if (count($unitsEqual) == 1 && $unitsEqual[0] == '') {
                                    continue;
                                }

                                if (count($unitsEqual) > count($unitsBase)) {
                                    unset($unitsEqual[0]);
                                }

                                if (isset($arrayEntryData['ou']) && !is_array($arrayEntryData['ou'])) {
                                    $arrayDepartment[] = [
                                        'dn'     => $arrayEntryData['dn'],
                                        'parent' => (isset($unitsEqual[1]))? implode(',', $unitsEqual) : '',
                                        'ou'     => trim($arrayEntryData['ou']),
                                        'users'  => 0
                                    ];
                                }
                            } while ($entry = ldap_next_entry($ldapcnn, $entry));
                        }
                    }
                }

                if (!$flagError) {
                    ldap_control_paged_result_response($ldapcnn, $searchResult, $cookie);
                }
            } while (($cookie !== null && $cookie != '') && !$flagError);

            $str = '';

            foreach ($arrayDepartment as $dep) {
                $str .= ' ' . $dep['ou'];
            }

            $this->log($ldapcnn, 'found '. count($arrayDepartment) . ' departments: ' . $str);

            //Return
            return $arrayDepartment;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Get the Userlist from a department based on the name
     * @param string $departmentName
     * @return array
     */
    public function getUsersFromDepartmentByName($departmentName)
    {
        $dFilter = "(&(" . $this->arrayObjectClassFilter["department"] . ")(ou=" . $departmentName . "))";

        $aUsers = array();
        $rbac = &RBAC::getSingleton();
        //$rbac->userObj = new RbacUsers();
        $rbac->authSourcesObj = new AuthenticationSource();
        $aAuthSource = $rbac->authSourcesObj->load($this->sAuthSource);

        if (is_null($this->ldapcnn)) {
            $this->ldapcnn = $this->ldapConnection($aAuthSource);
        }

        $ldapcnn = $this->ldapcnn;

        $oSearch = @ldap_search($ldapcnn, $aAuthSource["AUTH_SOURCE_BASE_DN"], $dFilter, $this->arrayAttributesForUser);

        if ($oError = ldap_errno($ldapcnn)) {
            return $aUsers;
        } else {
            if ($oSearch) {
                //get the departments from the ldap entries
                if (ldap_count_entries($ldapcnn, $oSearch) > 0) {
                    $oEntry = ldap_first_entry($ldapcnn, $oSearch);

                    do {
                        $aAttr = $this->ldapGetAttributes($ldapcnn, $oEntry);
                        $aUsers[] = $this->ldapGetUsersFromDepartment("GET", $aAttr["dn"]);
                    } while ($oEntry = ldap_next_entry($ldapcnn, $oEntry));
                    //$this->createDepartments ($aDepts);
                }
            }
            return $aUsers;
        }
    }

    /**
     * Check if the department exists and returns the PM UID
     * @param <type> $currentDN
     * @return <type>
     */
    public function getDepUidIfExistsDN($currentDN)
    {
        try {
            $oCriteria = new Criteria('workflow');
            $oCriteria->add(DepartmentPeer::DEP_STATUS, 'ACTIVE');
            $oCriteria->add(DepartmentPeer::DEP_LDAP_DN, $currentDN);

            $oDataset = DepartmentPeer::doSelectRS($oCriteria);
            $oDataset->setFetchmode(ResultSet::FETCHMODE_ASSOC);

            if ($oDataset->next()) {
                $aRow = $oDataset->getRow();

                return $aRow["DEP_UID"];
            }

            return "";
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * Get number of Users in each Department from the Database
     *
     * return array Return array with the number of Users in each Department from the Database
     */
    public function departmentsGetNumberOfUsersFromDb()
    {
        try {
            $arrayData = array();

            //Get data
            $criteria = new Criteria("workflow");

            $criteria->addSelectColumn(UsersPeer::DEP_UID);
            $criteria->addSelectColumn("COUNT(" . UsersPeer::DEP_UID . ") AS NUM_REC");
            $criteria->add(UsersPeer::USR_STATUS, "CLOSED", Criteria::NOT_EQUAL);
            $criteria->add(UsersPeer::DEP_UID, "", Criteria::NOT_EQUAL);
            $criteria->add(UsersPeer::DEP_UID, null, Criteria::ISNOTNULL);
            $criteria->addGroupByColumn(UsersPeer::DEP_UID);

            $rsCriteria = UsersPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(ResultSet::FETCHMODE_ASSOC);

            while ($rsCriteria->next()) {
                $row = $rsCriteria->getRow();

                $arrayData[$row["DEP_UID"]] = $row["NUM_REC"];
            }

            //Return
            return $arrayData;
        } catch (Exception $e) {
            throw $e;
        }
    }

    public function userIsTerminated($userUid, $sOuTerminated)
    {
        $terminated = false;
        $aLdapUsers = $this->getUsersFromDepartmentByName($sOuTerminated);

        foreach ($aLdapUsers as $aLdapUser) {
            if ($aLdapUser['sUsername'] == $userUid) {
                $terminated = true;
                break;
            }
        }

        return $terminated;
    }

    /* activate an user previously deactivated
      if user is now in another department, we need the second parameter, the depUid

      @param string $userUid
      @param string optional department DN
      @param string optional DepUid
    */
    public function activateUser($userUid, $userDn = null, $depUid = null)
    {
        if (!class_exists('RbacUsers')) {
            require_once(PATH_RBAC.'model/RbacUsers.php');
        }

        $con = Propel::getConnection(RbacUsersPeer::DATABASE_NAME);
        // select set
        $c1 = new Criteria('rbac');
        $c1->add(RbacUsersPeer::USR_UID, $userUid);
        // update set
        $c2 = new Criteria('rbac');
        $c2->add(RbacUsersPeer::USR_STATUS, '1');

        if ($userDn != null) {
            $c2->add(RbacUsersPeer::USR_AUTH_USER_DN, $userDn);
            $c2->add(RbacUsersPeer::USR_AUTH_SUPERVISOR_DN, '');
        }

        BasePeer::doUpdate($c1, $c2, $con);

        if (!class_exists('Users')) {
            require_once('classes/model/Users.php');
        }

        $con = Propel::getConnection(UsersPeer::DATABASE_NAME);
        // select set
        $c1 = new Criteria('workflow');
        $c1->add(UsersPeer::USR_UID, $userUid);
        // update set
        $c2 = new Criteria('workflow');
        $c2->add(UsersPeer::USR_STATUS, 'ACTIVE');

        if ($depUid != null) {
            $c2->add(UsersPeer::DEP_UID, $depUid);
        }

        BasePeer::doUpdate($c1, $c2, $con);
    }

    public function deactivateUser($userUid)
    {
        if (!class_exists('RbacUsers')) {
            require_once(PATH_RBAC.'model/RbacUsers.php');
        }

        $con = Propel::getConnection(RbacUsersPeer::DATABASE_NAME);
        // select set
        $c1 = new Criteria('rbac');
        $c1->add(RbacUsersPeer::USR_USERNAME, $userUid);
        // update set
        $c2 = new Criteria('rbac');
        $c2->add(RbacUsersPeer::USR_STATUS, '0');

        BasePeer::doUpdate($c1, $c2, $con);

        if (!class_exists('Users')) {
            require_once('classes/model/Users.php');
        }

        $con = Propel::getConnection(UsersPeer::DATABASE_NAME);
        // select set
        $c1 = new Criteria('workflow');
        $c1->add(UsersPeer::USR_USERNAME, $userUid);
        // update set
        $c2 = new Criteria('workflow');
        $c2->add(UsersPeer::USR_STATUS, 'INACTIVE');
        $c2->add(UsersPeer::DEP_UID, '');

        BasePeer::doUpdate($c1, $c2, $con);
    }

    public function getTerminatedOu()
    {
        if (trim($this->sAuthSource)!='') {
            $rbac = &RBAC::getSingleton();
            $aAuthSource = $rbac->authSourcesObj->load($this->sAuthSource);
            $attributes = $aAuthSource['AUTH_SOURCE_DATA'];
            $this->sTerminatedOu = isset($attributes['AUTH_SOURCE_RETIRED_OU'])?$attributes['AUTH_SOURCE_RETIRED_OU']:'';
        }

        return $this->sTerminatedOu;
    }

    /**
      get all authsource for this plugin ( ldapAdvanced plugin, because other authsources are not needed )
      this function is used only by cron
      returns only AUTH_SOURCE_PROVIDER = ldapAdvanced

      @return array with authsources with type = ldap
    */
    public function getAuthSources()
    {
        require_once(PATH_RBAC.'model/AuthenticationSource.php');

        $oCriteria = new Criteria('rbac');
        $aAuthSources = array();

        $oAuthSource = new AuthenticationSource();
        $oCriteria = $oAuthSource->getAllAuthSources();
        $oDataset = AuthenticationSourcePeer::doSelectRS($oCriteria);
        $oDataset->setFetchmode(ResultSet::FETCHMODE_ASSOC);

        while ($oDataset->next()) {
            $aRow = $oDataset->getRow();

            if ($aRow['AUTH_SOURCE_PROVIDER'] == 'ldapAdvanced') {
                $aRow["AUTH_SOURCE_DATA"] = ($aRow["AUTH_SOURCE_DATA"] != "")? unserialize($aRow["AUTH_SOURCE_DATA"]) : array();

                $aAuthSources[] = $aRow;
            }
        }

        return $aAuthSources;
    }

    /**
      function to get departments from the array previously obtained from LDAP
      we are calling registered departments
      it is a recursive function, in the first call with an array with first top level departments from PM
      then go thru all departments and obtain a list of departments already created in PM and pass that array
      to next function to synchronize All users for each department
      this function is used in cron only

      @param array departments obtained from LDAP/Active Directory
      @param array of departments, first call have only top level departments
    */
    public function getRegisteredDepartments(array $arrayLdapDepartment, array $arrayDbDepartment)
    {
        $aResult = array();

        if (!empty($arrayLdapDepartment)) {
            $arrayLdapDepartment[0]["ou"] = $arrayLdapDepartment[0]["ou"] . " " . $arrayLdapDepartment[0]["dn"]; //Discard ROOT

            foreach ($arrayLdapDepartment as $ldapDept) {
                //$flagExists = false;
                //
                //foreach ($aResult as $value2) {
                //    $arrayDepartmentData = $value2;
                //
                //    if ($arrayDepartmentData["DEP_LDAP_DN"] == $ldapDept["dn"]) {
                //        $flagExists = true;
                //        break;
                //    }
                //}
                //
                //if ($flagExists) {
                //    continue;
                //}

                foreach ($arrayDbDepartment as $department) {
                    if ($department["DEP_TITLE"] == $ldapDept["ou"] && $department["DEP_LDAP_DN"] == $ldapDept["dn"]) {
                        $aResult[] = $department;
                        break;

                        //if ($department["HAS_CHILDREN"] != 0) {
                        //    $aTempDepartments = $this->getDepartments($department["DEP_UID"]);
                        //    $aTempRegistered  = $this->getRegisteredDepartments($arrayLdapDepartment, $aTempDepartments);
                        //    $aResult = array_merge($aResult, $aTempRegistered);
                        //}
                    }
                }
            }
        }

        return $aResult;
    }

    /**
     select departments but it is not recursive, only returns departments in this level
     @param string $DepParent the DEP_UID for parent department
    */
    public function getDepartments($DepParent)
    {
        try {
            $result = array();
            $criteria = new Criteria('workflow');

            if (!empty($DepParent)) {
                $criteria->add(DepartmentPeer::DEP_PARENT, $DepParent);
            }

            $con = Propel::getConnection(DepartmentPeer::DATABASE_NAME);
            $objects = DepartmentPeer::doSelect($criteria, $con);

            foreach ($objects as $oDepartment) {
                $node = array();
                $node['DEP_UID']      = $oDepartment->getDepUid();
                $node['DEP_PARENT']   = $oDepartment->getDepParent();
                $node['DEP_TITLE']    = stripslashes($oDepartment->getDepTitle());
                $node['DEP_STATUS']   = $oDepartment->getDepStatus();
                $node['DEP_MANAGER']  = $oDepartment->getDepManager();
                $node['DEP_LDAP_DN']  = $oDepartment->getDepLdapDn();
                $node['DEP_LAST']     = 0;

                $criteriaCount = new Criteria('workflow');
                $criteriaCount->clearSelectColumns();
                $criteriaCount->addSelectColumn('COUNT(*)');
                $criteriaCount->add(DepartmentPeer::DEP_PARENT, $oDepartment->getDepUid(), Criteria::EQUAL);
                $rs = DepartmentPeer::doSelectRS($criteriaCount);
                $rs->next();
                $row = $rs->getRow();
                $node['HAS_CHILDREN'] = $row[0];
                $result[] = $node;
            }

            if (count($result) >= 1) {
                $result[ count($result) -1 ]['DEP_LAST'] = 1;
            }

            return $result;
        } catch (exception $e) {
            throw $e;
        }
    }

    /**
      function to get users from USERS table in wf_workflow and filter by department
      this function is used in cron only

      @param string department UID ( DEP_UID value )
      @return array of users
    */
    public function getUserFromPM($username)
    {
        try {
            $criteria = new Criteria("workflow");

            $criteria->addSelectColumn(UsersPeer::USR_UID);
            $criteria->addSelectColumn(UsersPeer::USR_USERNAME);
            $criteria->addSelectColumn(UsersPeer::DEP_UID);
            $criteria->add(UsersPeer::USR_STATUS, "CLOSED", Criteria::NOT_EQUAL);
            $criteria->add(UsersPeer::USR_USERNAME, $username, Criteria::EQUAL);

            $rsCriteria = UsersPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(ResultSet::FETCHMODE_ASSOC);

            if ($rsCriteria->next()) {
                return $rsCriteria->getRow();
            }

            return array();
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
      get all user (UID, USERNAME) moved to Removed OU
      this function is used in cron only

      @param array authSource row, in this fuction we are validating if Removed OU is defined or not
      @return array of users
    */
    public function getUsersFromRemovedOu($aAuthSource)
    {
        $aUsers = array(); //empty array is the default result
        $attributes = $aAuthSource["AUTH_SOURCE_DATA"];
        $this->sTerminatedOu = isset($attributes['AUTH_SOURCE_RETIRED_OU'])? trim($attributes['AUTH_SOURCE_RETIRED_OU']) : '';

        if ($this->sTerminatedOu == '') {
            return $aUsers;
        }

        return $this->getUsersFromDepartmentByName($this->sTerminatedOu);
    }

    /**
      set STATUS=0 for all users in the array $aUsers
      this functin is used to deactivate an array of users ( usually used for Removed OU )
      this function is used in cron only

      @param array authSource row, in this fuction we are validating if Removed OU is defined or not
      @return array of users
    */
    public function deactiveArrayOfUsers($aUsers)
    {
        if (!class_exists('RbacUsers')) {
            require_once(PATH_RBAC.'model/RbacUsers.php');
        }

        if (!class_exists('Users')) {
            require_once('classes/model/Users.php');
        }

        $aUsrUid = array();

        foreach ($aUsers as $key => $val) {
            $aUsrUid[] = $val['sUsername'];
        }

        $con = Propel::getConnection('rbac');
        // select set
        $c1 = new Criteria('rbac');
        $c1->add(RbacUsersPeer::USR_USERNAME, $aUsrUid, Criteria::IN);
        $c1->add(RbacUsersPeer::USR_STATUS, 1);
        // update set
        $c2 = new Criteria('rbac');
        $c2->add(RbacUsersPeer::USR_STATUS, '0');
        BasePeer::doUpdate($c1, $c2, $con);

        $con = Propel::getConnection('workflow');
        // select set
        $c1 = new Criteria('workflow');
        $c1->add(UsersPeer::USR_USERNAME, $aUsrUid, Criteria::IN);
        // update set
        $c2 = new Criteria('workflow');
        $c2->add(UsersPeer::USR_STATUS, 'INACTIVE');
        $c2->add(UsersPeer::DEP_UID, '');

        BasePeer::doUpdate($c1, $c2, $con);

        return true;
    }

    /**
      creates an users using the data send in the array $aUsers
      and then add the user to specific department
      this function is used in cron only

      @param array $aUser info taken from ldap
      @param string $depUid the department UID
      @return boolean
    */
    public function createUserAndActivate($aUser, $depUid)
    {
        $rbac = &RBAC::getSingleton();

        if ($rbac->userObj == null) {
            $rbac->userObj = new RbacUsers();
        }

        if ($rbac->rolesObj == null) {
            $rbac->rolesObj = new Roles();
        }

        if ($rbac->usersRolesObj == null) {
            $rbac->usersRolesObj = new UsersRoles();
        }

        $sUsername  = $aUser['sUsername'];
        $sFullname  = $aUser['sFullname'];
        $sFirstname = $aUser['sFirstname'];
        $sLastname  = $aUser['sLastname'];
        $sEmail     = $aUser['sEmail'];
        $sDn        = $aUser['sDN'];

        $aData = array();
        $aData['USR_USERNAME']     = $sUsername;
        $aData["USR_PASSWORD"]     = "00000000000000000000000000000000";
        $aData['USR_FIRSTNAME']    = $sFirstname;
        $aData['USR_LASTNAME']     = $sLastname;
        $aData['USR_EMAIL']        = $sEmail;
        $aData['USR_DUE_DATE']     = date('Y-m-d', mktime(0, 0, 0, date('m'), date('d'), date('Y') + 2));
        $aData['USR_CREATE_DATE']  = date('Y-m-d H:i:s');
        $aData['USR_UPDATE_DATE']  = date('Y-m-d H:i:s');
        $aData['USR_BIRTHDAY']     = date('Y-m-d');
        $aData['USR_STATUS']       = 1;
        $aData['USR_AUTH_TYPE']    = 'ldapadvanced';
        $aData['UID_AUTH_SOURCE']  = $this->sAuthSource;
        $aData['USR_AUTH_USER_DN'] = $sDn;

        $sUserUID = $rbac->createUser($aData, "PROCESSMAKER_OPERATOR");

        $aData['USR_STATUS']       = 'ACTIVE';
        $aData['USR_UID']          = $sUserUID;
        $aData['DEP_UID']          = $depUid;
        $aData['USR_ROLE']         = 'PROCESSMAKER_OPERATOR';

        require_once 'classes/model/Users.php';

        $oUser = new Users();
        $oUser->create($aData);

        return $sUserUID;
    }

    public function synchronizeManagers($managersHierarchy)
    {
        require_once 'classes/model/RbacUsers.php';

        try {
            foreach ($managersHierarchy as $managerDN => $subordinates) {
                $criteria = new Criteria('rbac');
                $criteria->addSelectColumn('*');
                $criteria->add(RbacUsersPeer::USR_AUTH_USER_DN, $managerDN);
                $dataset = RbacUsersPeer::doSelectRS($criteria);
                $dataset->setFetchmode(ResultSet::FETCHMODE_ASSOC);

                if ($dataset->next()) {
                    $row = $dataset->getRow();
                    $criteriaSet = new Criteria('workflow');
                    $criteriaSet->add(UsersPeer::USR_REPORTS_TO, $row['USR_UID']);
                    $criteriaWhere = new Criteria('workflow');
                    $criteriaWhere->add(UsersPeer::USR_UID, $subordinates, Criteria::IN);
                    BasePeer::doUpdate($criteriaWhere, $criteriaSet, Propel::getConnection('workflow'));
                }
            }
        } catch (Exception $error) {
            $this->log($this->ldapcnn, $error->getMessage());
        }
    }

    public function clearManager($usersUIDs)
    {
        try {
            $criteriaSet = new Criteria('workflow');
            $criteriaSet->add(UsersPeer::USR_REPORTS_TO, '');
            $criteriaWhere = new Criteria('workflow');
            $criteriaWhere->add(UsersPeer::USR_UID, $usersUIDs, Criteria::IN);
            BasePeer::doUpdate($criteriaWhere, $criteriaSet, Propel::getConnection('workflow'));
        } catch (Exception $error) {
            $this->log($this->ldapcnn, $error->getMessage());
        }
    }

    /**
     * Get a group list
     * @return <type>
     */
    public function searchGroups()
    {
        try {
            $arrayGroup = [];

            //Set variables
            $rbac = &RBAC::getSingleton();

            if (is_null($rbac->authSourcesObj)) {
                $rbac->authSourcesObj = new AuthenticationSource();
            }

            $arrayAuthenticationSourceData = $rbac->authSourcesObj->load($this->sAuthSource);

            if (is_null($this->ldapcnn)) {
                $this->ldapcnn = $this->ldapConnection($arrayAuthenticationSourceData);
            }

            $ldapcnn = $this->ldapcnn;

            //Get Groups
            $limit = $this->__getPageSizeLimitByData($arrayAuthenticationSourceData);
            $flagError = false;

            $filter = '(' . $this->arrayObjectClassFilter['group'] . ')';

            $this->log($ldapcnn, 'search groups with Filter: ' . $filter);

            $cookie = '';

            do {
                ldap_control_paged_result($ldapcnn, $limit, true, $cookie);

                $searchResult = @ldap_search($ldapcnn, $arrayAuthenticationSourceData['AUTH_SOURCE_BASE_DN'], $filter, ['dn', 'cn']);

                if ($error = ldap_errno($ldapcnn)) {
                    $this->log($ldapcnn, 'Error in Search');

                    $flagError = true;
                } else {
                    if ($searchResult) {
                        //Get groups from the ldap entries
                        $countEntries = ldap_count_entries($ldapcnn, $searchResult);

                        if ($countEntries > 0) {
                            $entry = ldap_first_entry($ldapcnn, $searchResult);

                            do {
                                $arrayEntryData = $this->ldapGetAttributes($ldapcnn, $entry);

                                if (isset($arrayEntryData['cn']) && !is_array($arrayEntryData['cn'])) {
                                    $arrayGroup[] = [
                                        'dn'    => $arrayEntryData['dn'],
                                        'cn'    => trim($arrayEntryData['cn']),
                                        'users' => 0,
                                    ];
                                }
                            } while ($entry = ldap_next_entry($ldapcnn, $entry));
                        }
                    }
                }

                if (!$flagError) {
                    ldap_control_paged_result_response($ldapcnn, $searchResult, $cookie);
                }
            } while (($cookie !== null && $cookie != '') && !$flagError);

            $str = '';

            foreach ($arrayGroup as $group) {
                $str .= ' ' . $group['cn'];
            }

            $this->log($ldapcnn, 'found '. count($arrayGroup) . ' groups: ' . $str);

            //Return
            return $arrayGroup;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Check if the group exists and returns the PM UID
     * @param <type> $currentDN
     * @return <type>
     */

    public function getGrpUidIfExistsDN($currentDN)
    {
        try {
            $criteria = new Criteria('workflow');
            $criteria->add(GroupwfPeer::GRP_STATUS, 'ACTIVE');
            $criteria->add(GroupwfPeer::GRP_LDAP_DN, $currentDN);
            $dataset = GroupwfPeer::doSelectRS($criteria);
            $dataset->setFetchmode(ResultSet::FETCHMODE_ASSOC);

            if ($dataset->next()) {
                $row = $dataset->getRow();

                return $row['GRP_UID'];
            }

            return "";
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * Get number of Users in each Group from the Database
     *
     * return array Return array with the number of Users in each Group from the Database
     */
    public function groupsGetNumberOfUsersFromDb()
    {
        try {
            $arrayData = array();

            //Get data
            $criteria = new Criteria("workflow");

            $criteria->addSelectColumn(GroupUserPeer::GRP_UID);
            $criteria->addSelectColumn("COUNT(" . GroupUserPeer::GRP_UID . ") AS NUM_REC");
            $criteria->addJoin(GroupUserPeer::USR_UID, UsersPeer::USR_UID, Criteria::LEFT_JOIN);
            $criteria->add(UsersPeer::USR_STATUS, "CLOSED", Criteria::NOT_EQUAL);
            $criteria->addGroupByColumn(GroupUserPeer::GRP_UID);

            $rsCriteria = GroupUserPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(ResultSet::FETCHMODE_ASSOC);

            while ($rsCriteria->next()) {
                $row = $rsCriteria->getRow();

                $arrayData[$row["GRP_UID"]] = $row["NUM_REC"];
            }

            //Return
            return $arrayData;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     select groups but it is not recursive, only returns groups in this level
    */
    public function getGroups()
    {
        try {
            $result = array();
            $criteria = new Criteria('workflow');
            $con = Propel::getConnection(GroupwfPeer::DATABASE_NAME);
            $objects = GroupwfPeer::doSelect($criteria, $con);

            foreach ($objects as $oGroup) {
                $node = array();
                $node['GRP_UID']      = $oGroup->getGrpUid();
                $node['GRP_TITLE']    = stripslashes($oGroup->getGrpTitle());
                $node['GRP_STATUS']   = $oGroup->getGrpStatus();
                $node['GRP_LDAP_DN']  = $oGroup->getGrpLdapDn();
                $result[] = $node;
            }

            return $result;
        } catch (exception $e) {
            throw $e;
        }
    }

    /**
      function to get groups from the array previously obtained from LDAP
      we are calling registered groups
      it is a recursive function, in the first call with an array with first top level groups from PM
      then go thru all groups and obtain a list of groups already created in PM and pass that array
      to next function to synchronize All users for each group
      this function is used in cron only

      @param array groups obtained from LDAP/Active Directory
      @param array of groups, first call have only top level groups
    */
    public function getRegisteredGroups(array $arrayLdapGroup, array $arrayDbGroup)
    {
        $aResult = array();

        if (!empty($arrayLdapGroup)) {
            foreach ($arrayLdapGroup as $ldapGroup) {
                foreach ($arrayDbGroup as $group) {
                    if ($group["GRP_TITLE"] == $ldapGroup["cn"] && $group["GRP_LDAP_DN"] == $ldapGroup["dn"]) {
                        $aResult[] = $group;
                    }
                }
            }
        }

        return $aResult;
    }

    /**
     * Convert 18-digit LDAP timestamps  to format PM
     *
     * @author Ronald Escobar <ronald@colosa.com>
     * @param Date | $dateAD | Date of AD ('Windows NT time format' and 'Win32 FILETIME or SYSTEMTIME')
     * @param Date | $datePM | Date of PM
     */
    public function convertDateADtoPM($dateAD)
    {
        $unixTimestamp = ($dateAD / 10000000) - 11644560000;
        $datePM  = date('Y-m-d', mktime(0, 0, 0, date('m'), '01', date('Y') + 2));//(date('Y') + 10)."-12-01";
        if ($unixTimestamp >0) {
            $dateAux = date("Y-m-d", $unixTimestamp);
            $yearAux = date("Y", $unixTimestamp);
            if (strlen(trim($yearAux)) <= 4) {
                $datePM  = $dateAux;
            }
        }
        return $datePM;
    }

    public function custom_ldap_explode_dn($dn)
    {
        $dn = trim($dn, ',');
        $result = ldap_explode_dn($dn, 0);

        if (is_array($result)) {
            unset($result['count']);

            foreach ($result as $key => $value) {
                $result[$key] = addcslashes(preg_replace("/\\\([0-9A-Fa-f]{2})/e", "''.chr(hexdec('\\1')).''", $value), '<>,"');
            }
        }

        return $result;
    }

    /**
     * Synchronize User for this Department
     *
     * @param string $departmentUid UID of Department
     * @param array  $arrayUserLdap User LDAP data
     * @param array  $arrayData     Data
     *
     * return array Return data
     */
    public function departmentSynchronizeUser($departmentUid, array $arrayUserLdap, array $arrayData)
    {
        try {
            $this->debugLog("class.ldapAdvanced.php > function departmentSynchronizeUser() > START");
            $this->debugLog("class.ldapAdvanced.php > function departmentSynchronizeUser() > \$arrayUserLdap[sUsername] ----> " . $arrayUserLdap["sUsername"]);

            $userUid = "";
            $found = false;

            $arrayUserData = $this->departmentGetUserDataIfUsernameExists($arrayUserLdap["sUsername"]);

            if (!empty($arrayUserData)) {
                //User already exists in this department and there is nothing to do
                //User already exists
                $userUid = $arrayUserData["USR_UID"];
                $found = true;

                $arrayData["already"]++;
                $arrayData["alreadyUsers"] .= $arrayUserData["USR_USERNAME"] . " ";
            }

            if (!$found) {
                //If user DO NOT exists in this department.. do:
                //If exists with another AuthSource -> impossible
                //If exists in another department, but in PM and for this authsource, we need to move it

                //$arrayNewUserData = $this->searchUserByUid($arrayUserLdap["sUsername"]);
                $arrayNewUserData = $arrayUserLdap;

                $arrayAux = $this->custom_ldap_explode_dn($arrayNewUserData["sDN"]);
                array_shift($arrayAux);

                $departmentUid = $this->getDepUidIfExistsDN(implode(",", $arrayAux)); //Check if exists the Department DN in DB

                $this->debugLog("class.ldapAdvanced.php > function departmentSynchronizeUser() > \$departmentUid ----> $departmentUid");

                if ($departmentUid != "") {
                    $arrayUserData = $this->authenticationSourceGetUserDataIfUsernameExists($arrayNewUserData["sUsername"]);

                    if (!empty($arrayUserData)) {
                        //User exists in this Authentication Source
                        //Move User
                        $userUid = $arrayUserData["USR_UID"];

                        $this->activateUser($arrayUserData["USR_UID"], $arrayNewUserData["sDN"], $departmentUid);

                        $arrayData["moved"]++;
                        $arrayData["movedUsers"] .= $arrayUserData["USR_USERNAME"] . " ";

                        $this->setArrayAuthenticationSourceUser($userUid, $arrayNewUserData); //INITIALIZE DATA //Update User
                    } else {
                        $arrayUserData = $this->getUserFromPM($arrayNewUserData["sUsername"]);

                        if (!empty($arrayUserData)) {
                            //User exists in another Authentication Source and another Department
                            //Impossible
                            $userUid = $arrayUserData["USR_UID"];

                            $arrayData["impossible"]++;
                            $arrayData["impossibleUsers"] .= $arrayUserData["USR_USERNAME"] . " ";
                        } else {
                            //User not exists
                            //Create User
                            $userUid = $this->createUserAndActivate($arrayNewUserData, $departmentUid);

                            $arrayData["created"]++;
                            $arrayData["createdUsers"] .= $arrayNewUserData["sUsername"] . " ";

                            $this->setArrayAuthenticationSourceUser($userUid, $arrayNewUserData); //INITIALIZE DATA //Add User
                        }
                    }
                }
            }

            if ($userUid != "") {
                $arrayData["arrayUserUid"][] = $userUid;

                if (isset($arrayUserLdap["sManagerDN"]) && $arrayUserLdap["sManagerDN"] != "") {
                    if (!isset($arrayData["managersHierarchy"][$arrayUserLdap["sManagerDN"]])) {
                        $arrayData["managersHierarchy"][$arrayUserLdap["sManagerDN"]] = array();
                    }

                    $arrayData["managersHierarchy"][$arrayUserLdap["sManagerDN"]][$userUid] = $userUid;
                }
            }

            $this->debugLog("class.ldapAdvanced.php > function departmentSynchronizeUser() > \$userUid ----> $userUid");
            $this->debugLog("class.ldapAdvanced.php > function departmentSynchronizeUser() > END");

            //Return
            return $arrayData;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Synchronize User for this Group
     *
     * @param string $groupUid      UID of Group
     * @param array  $arrayUserLdap User LDAP data
     * @param array  $arrayData     Data
     *
     * return array Return data
     */
    public function groupSynchronizeUser($groupUid, array $arrayUserLdap, array $arrayData)
    {
        try {
            $this->debugLog("class.ldapAdvanced.php > function groupSynchronizeUser() > START");
            $this->debugLog("class.ldapAdvanced.php > function groupSynchronizeUser() > \$arrayUserLdap[sUsername] ----> " . $arrayUserLdap["sUsername"]);

            $group = new Groups();

            $userUid = "";
            $found = false;

            $arrayUserData = $this->groupGetUserDataIfUsernameExists($arrayUserLdap["sUsername"]);

            if (!empty($arrayUserData)) {
                //User already exists in this group and there is nothing to do
                //User already exists
                $userUid = $arrayUserData["USR_UID"];
                $found = true;

                $arrayData["already"]++;
                $arrayData["alreadyUsers"] .= $arrayUserData["USR_USERNAME"] . " ";
            }

            if (!$found) {
                //If user DO NOT exists in this group.. do:
                //If exists with another AuthSource -> impossible
                //If exists in another group, but in PM and for this authsource, we need to move it

                //$arrayNewUserData = $this->searchUserByUid($arrayUserLdap["sUsername"]);
                $arrayNewUserData = $arrayUserLdap;

                $arrayUserData = $this->authenticationSourceGetUserDataIfUsernameExists($arrayNewUserData["sUsername"]);

                if (!empty($arrayUserData)) {
                    //User exists in this Authentication Source
                    //Move User
                    $userUid = $arrayUserData["USR_UID"];

                    $this->activateUser($arrayUserData["USR_UID"], $arrayNewUserData["sDN"]);

                    $group->addUserToGroup($groupUid, $userUid);

                    $arrayData["moved"]++;
                    $arrayData["movedUsers"] .= $arrayUserData["USR_USERNAME"] . " ";

                    $this->setArrayAuthenticationSourceUser($userUid, $arrayNewUserData); //INITIALIZE DATA //Update User
                } else {
                    $arrayUserData = $this->getUserFromPM($arrayNewUserData["sUsername"]);

                    if (!empty($arrayUserData)) {
                        //User exists in another Authentication Source and another Group
                        //Impossible
                        $userUid = $arrayUserData["USR_UID"];

                        $arrayData["impossible"]++;
                        $arrayData["impossibleUsers"] .= $arrayUserData["USR_USERNAME"] . " ";
                    } else {
                        //User not exists
                        //Create User
                        $userUid = $this->createUserAndActivate($arrayNewUserData, "");

                        $group->addUserToGroup($groupUid, $userUid);

                        $arrayData["created"]++;
                        $arrayData["createdUsers"] .= $arrayNewUserData["sUsername"] . " ";

                        $this->setArrayAuthenticationSourceUser($userUid, $arrayNewUserData); //INITIALIZE DATA //Add User
                    }
                }
            }

            if ($userUid != "") {
                $arrayData["arrayUserUid"][] = $userUid;

                if (isset($arrayUserLdap["sManagerDN"]) && $arrayUserLdap["sManagerDN"] != "") {
                    if (!isset($arrayData["managersHierarchy"][$arrayUserLdap["sManagerDN"]])) {
                        $arrayData["managersHierarchy"][$arrayUserLdap["sManagerDN"]] = array();
                    }

                    $arrayData["managersHierarchy"][$arrayUserLdap["sManagerDN"]][$userUid] = $userUid;
                }
            }

            $this->debugLog("class.ldapAdvanced.php > function groupSynchronizeUser() > \$userUid ----> $userUid");
            $this->debugLog("class.ldapAdvanced.php > function groupSynchronizeUser() > END");

            //Return
            return $arrayData;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Update User data based on the LDAP Server
     *
     * @param resource $ldapcnn             LDAP link identifier
     * @param array    $arrayAuthSourceData Authentication Source Data
     * @param string   $userDn              User DN
     * @param array    $arrayUser           Users
     *
     * @return bool
     */
    private function __ldapUserUpdateByDnAndData($ldapcnn, array $arrayAuthSourceData, $userDn, array $arrayUser)
    {
        try {
            //Set variables
            $rbac = &RBAC::getSingleton();

            if (is_null($rbac->userObj)) {
                $rbac->userObj = new RbacUsers();
            }

            //Set variables
            $flagUser = false;

            $arrayAttributesToSync = [
                //Default attributes to sync
                'USR_FIRSTNAME' => 'givenname',
                'USR_LASTNAME'  => 'sn',
                'USR_EMAIL'     => 'mail',
                'USR_STATUS'    => 'useraccountcontrol'
            ];

            if (isset($arrayAuthSourceData['AUTH_SOURCE_DATA']['AUTH_SOURCE_GRID_ATTRIBUTE']) &&
                !empty($arrayAuthSourceData['AUTH_SOURCE_DATA']['AUTH_SOURCE_GRID_ATTRIBUTE'])
            ) {
                foreach ($arrayAuthSourceData['AUTH_SOURCE_DATA']['AUTH_SOURCE_GRID_ATTRIBUTE'] as $value) {
                    $arrayAttributesToSync[$value['attributeUser']] = $value['attributeLdap'];
                }
            }

            //Search User from LDAP Server
            $uidUserIdentifier = (isset($arrayAuthSourceData['AUTH_SOURCE_DATA']['AUTH_SOURCE_IDENTIFIER_FOR_USER']))?
                $arrayAuthSourceData['AUTH_SOURCE_DATA']['AUTH_SOURCE_IDENTIFIER_FOR_USER'] : 'uid';

            $arrayAttribute = array_merge($this->arrayAttributesForUser, array_values($arrayAttributesToSync));

            $searchResult = @ldap_search($ldapcnn, $userDn, '(objectclass=*)', $arrayAttribute);

            if ($error = ldap_errno($ldapcnn)) {
                //
            } else {
                if ($searchResult && ldap_count_entries($ldapcnn, $searchResult) > 0) {
                    $entry = ldap_first_entry($ldapcnn, $searchResult);

                    $arrayUserLdap = $this->ldapGetAttributes($ldapcnn, $entry);

                    $username = (isset($arrayUserLdap[$uidUserIdentifier]))? $arrayUserLdap[$uidUserIdentifier] : '';

                    if ((is_array($username) && !empty($username)) || trim($username) != '') {
                        $username = trim((is_array($username))? $username[0] : $username);

                        if (isset($arrayUser[$username])) {
                            if (!isset($this->arrayUserUpdateChecked[$username])) {
                                $this->arrayUserUpdateChecked[$username] = 1;

                                $arrayUserDataUpdate = [];

                                foreach ($arrayAttributesToSync as $key => $value) {
                                    $fieldName = $key;
                                    $attributeName = strtolower($value);

                                    if (isset($arrayUserLdap[$attributeName])) {
                                        $ldapAttributeValue = trim((is_array($arrayUserLdap[$attributeName]))? $arrayUserLdap[$attributeName][0] : $arrayUserLdap[$attributeName]);

                                        switch ($fieldName) {
                                            case 'USR_STATUS':
                                                if ($attributeName == 'useraccountcontrol') {
                                                    $ldapAttributeValue = (preg_match('/^(?:' . '512|544|66048|66080' . ')$/', $ldapAttributeValue))? (($arrayUser[$username][$fieldName] == 'VACATION')? 'VACATION' : 'ACTIVE') : 'INACTIVE';
                                                }
                                                break;
                                            case 'USR_DUE_DATE':
                                                if ($attributeName == 'accountexpires') {
                                                    $ldapAttributeValue = $this->convertDateADtoPM($ldapAttributeValue);
                                                }
                                                break;
                                        }

                                        if ($ldapAttributeValue != $arrayUser[$username][$fieldName]) {
                                            $arrayUserDataUpdate[$fieldName] = $ldapAttributeValue;
                                        }
                                    }
                                }

                                if (!empty($arrayUserDataUpdate)) {
                                    $arrayUserDataUpdate['USR_UID'] = $arrayUser[$username]['USR_UID'];

                                    //Update User data
                                    $rbac->updateUser($arrayUserDataUpdate);

                                    $user = new Users();
                                    $result = $user->update($arrayUserDataUpdate);
                                }
                            } else {
                                $this->log(
                                    $ldapcnn,
                                    'User is repeated: Username "' . $username .'", DN "' . $arrayUserLdap['dn'] . '"'
                                );
                            }

                            $flagUser = true;
                        }
                    }
                }
            }

            //Return
            return $flagUser;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Update Users data based on the LDAP Server
     *
     * @param resource $ldapcnn             LDAP link identifier
     * @param array    $arrayAuthSourceData Authentication Source Data
     * @param string   $filterUsers         Filter
     * @param array    $arrayUserUid        UID of Users
     * @param array    $arrayData           Data
     *
     * @return array
     */
    private function __ldapUsersUpdateData(
        $ldapcnn,
        array $arrayAuthSourceData,
        $filterUsers,
        array $arrayUserUid,
        array $arrayData
    ) {
        try {
            $totalUser = $arrayData['totalUser'];
            $countUser = $arrayData['countUser'];

            //Search Users
            $filter = '(&(' . $this->arrayObjectClassFilter['user'] . ')(|' . $filterUsers . '))';

            $searchResult = @ldap_search($ldapcnn, $arrayAuthSourceData['AUTH_SOURCE_BASE_DN'], $filter, $this->arrayAttributesForUser);

            if ($error = ldap_errno($ldapcnn)) {
                //
            } else {
                if ($searchResult && ldap_count_entries($ldapcnn, $searchResult) > 0) {
                    //Get Users from DB
                    $arrayUser = [];

                    $criteria = new Criteria('workflow');

                    $criteria->add(UsersPeer::USR_UID, $arrayUserUid, Criteria::IN);
                    //$criteria->add(UsersPeer::USR_USERNAME, '', Criteria::NOT_EQUAL);
                    $criteria->add(UsersPeer::USR_STATUS, 'CLOSED', Criteria::NOT_EQUAL);

                    $rsCriteria = UsersPeer::doSelectRS($criteria);
                    $rsCriteria->setFetchmode(ResultSet::FETCHMODE_ASSOC);

                    while ($rsCriteria->next()) {
                        $row = $rsCriteria->getRow();

                        $arrayUser[$row['USR_USERNAME']] = $row;
                    }

                    //Get Users from LDAP Server
                    $entry = ldap_first_entry($ldapcnn, $searchResult);

                    do {
                        if ($this->__ldapUserUpdateByDnAndData(
                                $ldapcnn, $arrayAuthSourceData, ldap_get_dn($ldapcnn, $entry), $arrayUser
                            )
                        ) {
                            $countUser++;

                            //Progress bar
                            $this->frontEndShow(
                                'BAR',
                                'Update Users data: ' . $countUser . '/' . $totalUser . ' ' . $this->progressBar($totalUser, $countUser)
                            );
                        }
                    } while ($entry = ldap_next_entry($ldapcnn, $entry));
                }
            }

            //Return
            return [$totalUser, $countUser];
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Update Users data based on the LDAP Server
     *
     * @param string $authenticationSourceUid UID of Authentication Source
     *
     * return void
     */
    public function usersUpdateData($authenticationSourceUid)
    {
        try {
            $totalUser = count($this->arrayAuthenticationSourceUsersByUid);
            $countUser = 0;

            //Set variables
            $rbac = &RBAC::getSingleton();

            if (is_null($rbac->authSourcesObj)) {
                $rbac->authSourcesObj = new AuthenticationSource();
            }

            $arrayAuthenticationSourceData = $rbac->authSourcesObj->load($authenticationSourceUid);

            $this->ldapcnn = $this->ldapConnection($arrayAuthenticationSourceData);

            $ldapcnn = $this->ldapcnn;

            //Update Users
            $limit = $this->__getPageSizeLimitByData($arrayAuthenticationSourceData);
            $count = 0;

            $uidUserIdentifier = (isset($arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_IDENTIFIER_FOR_USER"]))? $arrayAuthenticationSourceData["AUTH_SOURCE_DATA"]["AUTH_SOURCE_IDENTIFIER_FOR_USER"] : "uid";

            $filterUsers = "";
            $arrayUserUid = array();

            foreach ($this->arrayAuthenticationSourceUsersByUid as $value) {
                $arrayUserData = $value;

                $count++;

                $filterUsers .= "($uidUserIdentifier=" . $arrayUserData["USR_USERNAME"] . ")";
                $arrayUserUid[] = $arrayUserData["USR_UID"];

                if ($count == $limit) {
                    list($totalUser, $countUser) = $this->__ldapUsersUpdateData(
                        $ldapcnn, $arrayAuthenticationSourceData, $filterUsers, $arrayUserUid, ['totalUser' => $totalUser, 'countUser' => $countUser]
                    );

                    $count = 0;

                    $filterUsers = "";
                    $arrayUserUid = array();
                }
            }

            if ($count > 0) {
                list($totalUser, $countUser) = $this->__ldapUsersUpdateData(
                    $ldapcnn, $arrayAuthenticationSourceData, $filterUsers, $arrayUserUid, ['totalUser' => $totalUser, 'countUser' => $countUser]
                );
            }
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Get page size limit for a search result
     *
     * @param array $arrayAuthSourceData Authentication Source Data
     *
     * @return int Returns the page size limit for a search result
     */
    private function __getPageSizeLimitByData(array $arrayAuthSourceData)
    {
        if (isset($arrayAuthSourceData['AUTH_SOURCE_DATA']['LDAP_PAGE_SIZE_LIMIT'])) {
            return $arrayAuthSourceData['AUTH_SOURCE_DATA']['LDAP_PAGE_SIZE_LIMIT'];
        } else {
            return $this->getPageSizeLimit(false);
        }
    }

    /**
     * Get page size limit for a search result
     *
     * @param resource $ldapcnn LDAP link identifier
     * @param string   $baseDn  The base DN for the directory
     *
     * @return int Returns the page size limit for a search result
     */
    public function getPageSizeLimit($ldapcnn, $baseDn = '')
    {
        try {
            $limit = 1000;

            if ($ldapcnn === false) {
                return $limit;
            }

            $searchResult = @ldap_search($ldapcnn, $baseDn, '(|(objectclass=*))', ['dn']);

            if ($searchResult) {
                $countEntries = ldap_count_entries($ldapcnn, $searchResult);

                if ($countEntries > 0) {
                    $limit = ($countEntries > $limit)? $limit : $countEntries;
                }
            }

            //Return
            return $limit;
        } catch (Exception $e) {
            throw $e;
        }
    }
}
