";
}
}
/**
* Show LDAP users to add or synchronise
*
* @return void
*/
static function showLdapUsers() {
$values = [
'order' => 'DESC',
'start' => 0,
];
foreach ($_SESSION['ldap_import'] as $option => $value) {
$values[$option] = $value;
}
$rand = mt_rand();
$results = [];
$limitexceeded = false;
$ldap_users = self::getUsers($values, $results, $limitexceeded);
$config_ldap = new AuthLDAP();
$config_ldap->getFromDB($values['authldaps_id']);
if (is_array($ldap_users)) {
$numrows = count($ldap_users);
if ($numrows > 0) {
self::displaySizeLimitWarning($limitexceeded);
Html::printPager($values['start'], $numrows, $_SERVER['PHP_SELF'], '');
// delete end
array_splice($ldap_users, $values['start'] + $_SESSION['glpilist_limit']);
// delete begin
if ($values['start'] > 0) {
array_splice($ldap_users, 0, $values['start']);
}
$form_action = '';
$textbutton = '';
if ($_SESSION['ldap_import']['mode']) {
$textbutton = _x('button', 'Synchronize');
$form_action = __CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'sync';
} else {
$textbutton = _x('button', 'Import');
$form_action = __CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'import';
}
Html::openMassiveActionsForm('mass'.__CLASS__.$rand);
$massiveactionparams = ['num_displayed' => min(count($ldap_users),
$_SESSION['glpilist_limit']),
'container' => 'mass'.__CLASS__.$rand,
'specific_actions' => [$form_action => $textbutton]];
Html::showMassiveActions($massiveactionparams);
echo "
";
echo "";
echo "";
echo Html::getCheckAllAsCheckbox('mass'.__CLASS__.$rand);
echo " | ";
$num = 0;
if ($config_ldap->isSyncFieldEnabled()) {
echo Search::showHeaderItem(Search::HTML_OUTPUT, __('Synchronization field'), $num,
$_SERVER['PHP_SELF'].
"?order=".($values['order']=="DESC"?"ASC":"DESC"));
}
echo Search::showHeaderItem(Search::HTML_OUTPUT, _n('User', 'Users', Session::getPluralNumber()), $num,
$_SERVER['PHP_SELF'].
"?order=".($values['order']=="DESC"?"ASC":"DESC"));
echo "".__('Last update in the LDAP directory')." | ";
if ($_SESSION['ldap_import']['mode']) {
echo "".__('Last update in GLPI')." | ";
}
echo "
";
foreach ($ldap_users as $userinfos) {
echo "";
//Need to use " instead of ' because it doesn't work with names with ' inside !
echo "";
echo Html::getMassiveActionCheckBox(__CLASS__, $userinfos['uid']);
echo " | ";
if ($config_ldap->isSyncFieldEnabled()) {
echo "" . $userinfos['uid'] . " | ";
}
echo "";
if (isset($userinfos['id']) && User::canView()) {
echo "". $userinfos['name'] . "";
} else {
echo $userinfos['link'];
}
echo " | ";
if ($userinfos['stamp'] != '') {
echo "" .Html::convDateTime(date("Y-m-d H:i:s", $userinfos['stamp'])). " | ";
} else {
echo " | ";
}
if ($_SESSION['ldap_import']['mode']) {
if ($userinfos['date_sync'] != '') {
echo "" . Html::convDateTime($userinfos['date_sync']) . " | ";
}
}
echo "
";
}
echo "";
echo "";
echo Html::getCheckAllAsCheckbox('mass'.__CLASS__.$rand);
echo " | ";
$num = 0;
if ($config_ldap->isSyncFieldEnabled()) {
echo Search::showHeaderItem(Search::HTML_OUTPUT, __('Synchronization field'), $num,
$_SERVER['PHP_SELF'].
"?order=".($values['order']=="DESC"?"ASC":"DESC"));
}
echo Search::showHeaderItem(Search::HTML_OUTPUT, _n('User', 'Users', Session::getPluralNumber()), $num,
$_SERVER['PHP_SELF'].
"?order=".($values['order']=="DESC"?"ASC":"DESC"));
echo "".__('Last update in the LDAP directory')." | ";
if ($_SESSION['ldap_import']['mode']) {
echo "".__('Last update in GLPI')." | ";
}
echo "
";
echo "
";
$massiveactionparams['ontop'] = false;
Html::showMassiveActions($massiveactionparams);
Html::closeForm();
Html::printPager($values['start'], $numrows, $_SERVER['PHP_SELF'], '');
} else {
echo "
".
($_SESSION['ldap_import']['mode']?__('No user to be synchronized')
:__('No user to be imported'))."
";
}
} else {
echo "
".
($_SESSION['ldap_import']['mode']?__('No user to be synchronized')
:__('No user to be imported'))."
";
}
}
/**
* Search users
*
* @param resource $ds An LDAP link identifier
* @param array $values values to search
* @param string $filter search filter
* @param array $attrs An array of the required attributes
* @param boolean $limitexceeded is limit exceeded
* @param array $user_infos user informations
* @param array $ldap_users ldap users
* @param object $config_ldap ldap configuration
*
* @return boolean
*/
static function searchForUsers($ds, $values, $filter, $attrs, &$limitexceeded, &$user_infos,
&$ldap_users, $config_ldap) {
//If paged results cannot be used (PHP < 5.4)
$cookie = ''; //Cookie used to perform query using pages
$count = 0; //Store the number of results ldap_search
do {
if (self::isLdapPageSizeAvailable($config_ldap)) {
ldap_control_paged_result($ds, $config_ldap->fields['pagesize'], true, $cookie);
}
$filter = Toolbox::unclean_cross_side_scripting_deep(Toolbox::stripslashes_deep($filter));
$sr = @ldap_search($ds, $values['basedn'], $filter, $attrs);
if ($sr) {
if (in_array(ldap_errno($ds), [4,11])) {
// openldap return 4 for Size limit exceeded
$limitexceeded = true;
}
$info = self::get_entries_clean($ds, $sr);
if (in_array(ldap_errno($ds), [4,11])) {
$limitexceeded = true;
}
$count += $info['count'];
//If page results are enabled and the number of results is greater than the maximum allowed
//warn user that limit is exceeded and stop search
if (self::isLdapPageSizeAvailable($config_ldap)
&& $config_ldap->fields['ldap_maxlimit']
&& ($count > $config_ldap->fields['ldap_maxlimit'])) {
$limitexceeded = true;
break;
}
$field_for_sync = $config_ldap->getLdapIdentifierToUse();
$login_field = $config_ldap->fields['login_field'];
for ($ligne = 0; $ligne < $info["count"]; $ligne++) {
if (in_array($field_for_sync, $info[$ligne])) {
$uid = self::getFieldValue($info[$ligne], $field_for_sync);
if ($login_field != $field_for_sync && !isset($info[$ligne][$login_field])) {
Toolbox::logWarning("Missing field $login_field for LDAP entry $field_for_sync $uid");
//Login field may be missing... Skip the user
continue;
}
$user_infos[$uid]["timestamp"] = self::ldapStamp2UnixStamp(
$info[$ligne]['modifytimestamp'][0],
$config_ldap->fields['time_offset']
);
$user_infos[$uid]["user_dn"] = $info[$ligne]['dn'];
$user_infos[$uid][$field_for_sync] = $uid;
if ($config_ldap->isSyncFieldEnabled()) {
$user_infos[$uid][$login_field] = $info[$ligne][$login_field][0];
}
if ($values['mode'] == self::ACTION_IMPORT) {
//If ldap add
$ldap_users[$uid] = $uid;
} else {
//If ldap synchronisation
$ldap_users[$uid] = self::ldapStamp2UnixStamp(
$info[$ligne]['modifytimestamp'][0],
$config_ldap->fields['time_offset']
);
$user_infos[$uid]["name"] = $info[$ligne][$login_field][0];
}
}
}
} else {
return false;
}
if (self::isLdapPageSizeAvailable($config_ldap)) {
ldap_control_paged_result_response($ds, $sr, $cookie);
}
} while (($cookie !== null) && ($cookie != ''));
return true;
}
/**
* Get the list of LDAP users to add/synchronize
*
* @param array $options possible options:
* - authldaps_id ID of the server to use
* - mode user to synchronise or add?
* - ldap_filter ldap filter to use
* - basedn force basedn (default authldaps_id one)
* - order display order
* - begin_date begin date to time limit
* - end_date end date to time limit
* - script true if called by an external script
* @param array $results result stats
* @param boolean $limitexceeded limit exceeded exception
*
* @return array of the user
*/
static function getAllUsers(array $options, &$results, &$limitexceeded) {
global $DB;
$config_ldap = new self();
$res = $config_ldap->getFromDB($options['authldaps_id']);
$values = [
'order' => 'DESC',
'mode' => self::ACTION_SYNCHRONIZE,
'ldap_filter' => '',
'basedn' => $config_ldap->fields['basedn'],
'begin_date' => null,
'end_date' => date('Y-m-d H:i:s', time()-DAY_TIMESTAMP),
'script' => 0, //Called by an external script or not
];
foreach ($options as $option => $value) {
// this test break mode detection - if ($value != '') {
$values[$option] = $value;
//}
}
$ldap_users = [];
$user_infos = [];
$limitexceeded = false;
// we prevent some delay...
if (!$res) {
return false;
}
if ($values['order'] != "DESC") {
$values['order'] = "ASC";
}
$ds = $config_ldap->connect();
$field_for_sync = $config_ldap->getLdapIdentifierToUse();
$field_for_db = $config_ldap->getDatabaseIdentifierToUse();
if ($ds) {
//Search for ldap login AND modifyTimestamp,
//which indicates the last update of the object in directory
$attrs = [$config_ldap->fields['login_field'], "modifyTimestamp"];
if ($field_for_sync != $config_ldap->fields['login_field']) {
$attrs[] = $field_for_sync;
}
// Try a search to find the DN
if ($values['ldap_filter'] == '') {
$filter = "(".$field_for_sync."=*)";
if (!empty($config_ldap->fields['condition'])) {
$filter = "(& $filter ".$config_ldap->fields['condition'].")";
}
} else {
$filter = $values['ldap_filter'];
}
if ($values['script'] && !empty($values['begin_date'])) {
$filter_timestamp = self::addTimestampRestrictions($values['begin_date'],
$values['end_date']);
$filter = "(&$filter $filter_timestamp)";
}
$result = self::searchForUsers($ds, $values, $filter, $attrs, $limitexceeded,
$user_infos, $ldap_users, $config_ldap);
if (!$result) {
return false;
}
} else {
return false;
}
$glpi_users = [];
$select = [
'FROM' => User::getTable(),
'ORDER' => ['name ' . $values['order']]
];
if ($values['mode'] != self::ACTION_IMPORT) {
$select['WHERE'] = [
'authtype' => [-1, Auth::NOT_YET_AUTHENTIFIED, Auth::LDAP, Auth::EXTERNAL, Auth::CAS],
'auths_id' => $options['authldaps_id']
];
}
$iterator = $DB->request($select);
while ($user = $iterator->next()) {
$tmpuser = new User();
//Ldap add : fill the array with the login of the user
if ($values['mode'] == self::ACTION_IMPORT) {
$glpi_users[$user['name']] = $user['name'];
} else {
//Ldap synchronisation : look if the user exists in the directory
//and compares the modifications dates (ldap and glpi db)
$userfound = self::dnExistsInLdap($user_infos, $user['user_dn']);
if (!empty($ldap_users[$user[$field_for_db]]) || $userfound) {
// userfound seems that user dn is present in GLPI DB but do not correspond to an GLPI user
// -> renaming case
if ($userfound) {
//Get user in DB with this dn
if (!$tmpuser->getFromDBByDn(Toolbox::addslashes_deep($user['user_dn']))) {
//This should never happened
//If a user_dn is present more than one time in database
//Just skip user synchronization to avoid errors
continue;
}
$glpi_users[] = ['id' => $user['id'],
'user' => $userfound['name'],
$field_for_sync => (isset($userfound[$config_ldap->fields['sync_field']]) ? $userfound[$config_ldap->fields['sync_field']] : 'NULL'),
'timestamp' => $user_infos[$userfound[$field_for_sync]]['timestamp'],
'date_sync' => $tmpuser->fields['date_sync'],
'dn' => $user['user_dn']];
} else if (($values['mode'] == self::ACTION_ALL)
|| (($ldap_users[$user[$field_for_db]] - strtotime($user['date_sync'])) > 0)) {
//If entry was modified or if script should synchronize all the users
$glpi_users[] = ['id' => $user['id'],
'user' => $user['name'],
$field_for_sync => $user['sync_field'],
'timestamp' => $user_infos[$user[$field_for_db]]['timestamp'],
'date_sync' => $user['date_sync'],
'dn' => $user['user_dn']];
}
} else if (($values['mode'] == self::ACTION_ALL)
&& !$limitexceeded) {
// Only manage deleted user if ALL (because of entity visibility in delegated mode)
//If user is marked as coming from LDAP, but is not present in it anymore
if (!$user['is_deleted']
&& ($user['auths_id'] == $options['authldaps_id'])) {
User::manageDeletedUserInLdap($user['id']);
$results[self::USER_DELETED_LDAP] ++;
}
}
}
}
//If add, do the difference between ldap users and glpi users
if ($values['mode'] == self::ACTION_IMPORT) {
$diff = array_diff_ukey($ldap_users, $glpi_users, 'strcasecmp');
$list = [];
$tmpuser = new User();
foreach ($diff as $user) {
//If user dn exists in DB, it means that user login field has changed
if (!$tmpuser->getFromDBByDn(toolbox::addslashes_deep($user_infos[$user]["user_dn"]))) {
$entry = ["user" => $user_infos[$user][$config_ldap->fields['login_field']],
"timestamp" => $user_infos[$user]["timestamp"],
"date_sync" => Dropdown::EMPTY_VALUE];
if ($config_ldap->isSyncFieldEnabled()) {
$entry[$field_for_sync] = $user_infos[$user][$field_for_sync];
}
$list[] = $entry;
}
}
if ($values['order'] == 'DESC') {
rsort($list);
} else {
sort($list);
}
return $list;
}
return $glpi_users;
}
/**
* Check if a user DN exists in a ldap user search result
*
* @since 0.84
*
* @param array $ldap_infos ldap user search result
* @param string $user_dn user dn to look for
*
* @return boolean false if the user dn doesn't exist, user ldap infos otherwise
*/
static function dnExistsInLdap($ldap_infos, $user_dn) {
$found = false;
foreach ($ldap_infos as $ldap_info) {
if ($ldap_info['user_dn'] == $user_dn) {
$found = $ldap_info;
break;
}
}
return $found;
}
/**
* Show LDAP groups to add or synchronize in an entity
*
* @param string $target target page for the form
* @param integer $start where to start the list
* @param integer $sync synchronize or add? (default 0)
* @param string $filter ldap filter to use (default '')
* @param string $filter2 second ldap filter to use (which case?) (default '')
* @param integer $entity working entity
* @param string $order display order (default DESC)
*
* @return void
*/
static function showLdapGroups($target, $start, $sync = 0, $filter = '', $filter2 = '',
$entity = 0, $order = 'DESC') {
echo "
";
$limitexceeded = false;
$ldap_groups = self::getAllGroups($_SESSION["ldap_server"], $filter, $filter2, $entity,
$limitexceeded, $order);
if (is_array($ldap_groups)) {
$numrows = count($ldap_groups);
$rand = mt_rand();
if ($numrows > 0) {
self::displaySizeLimitWarning($limitexceeded);
$parameters = '';
Html::printPager($start, $numrows, $target, $parameters);
// delete end
array_splice($ldap_groups, $start + $_SESSION['glpilist_limit']);
// delete begin
if ($start > 0) {
array_splice($ldap_groups, 0, $start);
}
echo "
";
Html::openMassiveActionsForm('mass'.__CLASS__.$rand);
$massiveactionparams
= ['num_displayed'
=> min($_SESSION['glpilist_limit'], count($ldap_groups)),
'container'
=> 'mass'.__CLASS__.$rand,
'specific_actions'
=> [__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'import_group'
=> _sx('button', 'Import')],
'extraparams'
=> ['massive_action_fields' => ['dn', 'ldap_import_type',
'ldap_import_entities',
'ldap_import_recursive']]];
Html::showMassiveActions($massiveactionparams);
echo "
";
echo "";
echo "";
Html::showCheckbox(['criterion' => ['tag_for_massive' => 'select_item']]);
echo " | ";
$header_num = 0;
echo Search::showHeaderItem(Search::HTML_OUTPUT, __('Group'), $header_num,
$target."?order=".($order=="DESC"?"ASC":"DESC"),
1, $order);
echo "".__('Group DN')." | ";
echo "".__('Destination entity')." | ";
if (Session::isMultiEntitiesMode()) {
echo"";
Html::showCheckbox(['criterion' => ['tag_for_massive' => 'select_item_child_entities']]);
echo " ".__('Child entities');
echo " | ";
}
echo "
";
$dn_index = 0;
foreach ($ldap_groups as $groupinfos) {
$group = $groupinfos["cn"];
$group_dn = $groupinfos["dn"];
$search_type = $groupinfos["search_type"];
echo "";
echo "";
echo Html::hidden("dn[$dn_index]", ['value' => $group_dn,
'data-glpicore-ma-tags' => 'common']);
echo Html::hidden("ldap_import_type[$dn_index]", ['value' => $search_type,
'data-glpicore-ma-tags' => 'common']);
Html::showMassiveActionCheckBox(__CLASS__, $dn_index,
['massive_tags' => 'select_item']);
echo " | ";
echo "" . $group . " | ";
echo "" .$group_dn. " | ";
echo "";
Entity::dropdown(['value' => $entity,
'name' => "ldap_import_entities[$dn_index]",
'specific_tags' => ['data-glpicore-ma-tags' => 'common']]);
echo " | ";
if (Session::isMultiEntitiesMode()) {
echo "";
Html::showMassiveActionCheckBox(__CLASS__, $dn_index,
['massive_tags' => 'select_item_child_entities',
'name' => "ldap_import_recursive[$dn_index]",
'specific_tags' => ['data-glpicore-ma-tags' => 'entities_id']]);
echo " | ";
} else {
echo Html::hidden("ldap_import_recursive[$dn_index]", ['value' => 0,
'data-glpicore-ma-tags' => 'entities_id']);
}
echo "
\n";
$dn_index++;
}
$massiveactionparams['ontop'] = false;
Html::showMassiveActions($massiveactionparams);
Html::closeForm();
echo "";
Html::printPager($start, $numrows, $target, $parameters);
} else {
echo "" . __('No group to be imported') . "
";
}
} else {
echo "" . __('No group to be imported') . "
";
}
}
/**
* Get all LDAP groups from a ldap server which are not already in an entity
*
* @since 0.84 new parameter $limitexceeded
*
* @param integer $auths_id ID of the server to use
* @param string $filter ldap filter to use
* @param string $filter2 second ldap filter to use if needed
* @param string $entity entity to search
* @param boolean $limitexceeded is limit exceeded
* @param string $order order to use (default DESC)
*
* @return array of the groups
*/
static function getAllGroups($auths_id, $filter, $filter2, $entity, &$limitexceeded,
$order = 'DESC') {
global $DB;
$config_ldap = new self();
$config_ldap->getFromDB($auths_id);
$infos = [];
$groups = [];
$ds = $config_ldap->connect();
if ($ds) {
switch ($config_ldap->fields["group_search_type"]) {
case self::GROUP_SEARCH_USER:
$infos = self::getGroupsFromLDAP($ds, $config_ldap, $filter,
$limitexceeded, false, $infos);
break;
case self::GROUP_SEARCH_GROUP:
$infos = self::getGroupsFromLDAP($ds, $config_ldap, $filter,
$limitexceeded, true, $infos);
break;
case self::GROUP_SEARCH_BOTH:
$infos = self::getGroupsFromLDAP($ds, $config_ldap, $filter,
$limitexceeded, true, $infos);
$infos = self::getGroupsFromLDAP($ds, $config_ldap, $filter2,
$limitexceeded, false, $infos);
break;
}
if (!empty($infos)) {
$glpi_groups = [];
//Get all groups from GLPI DB for the current entity and the subentities
$iterator = $DB->request([
'SELECT' => ['name'],
'FROM' => 'glpi_groups',
'WHERE' => getEntitiesRestrictCriteria('glpi_groups')
]);
//If the group exists in DB -> unset it from the LDAP groups
while ($group = $iterator->next()) {
$glpi_groups[$group["name"]] = 1;
}
$ligne = 0;
foreach ($infos as $dn => $info) {
if (!isset($glpi_groups[$info["cn"]])) {
$groups[$ligne]["dn"] = $dn;
$groups[$ligne]["cn"] = $info["cn"];
$groups[$ligne]["search_type"] = $info["search_type"];
$ligne++;
}
}
}
if ($order == 'DESC') {
function local_cmp($b, $a) {
return strcasecmp($a['cn'], $b['cn']);
}
} else {
function local_cmp($a, $b) {
return strcasecmp($a['cn'], $b['cn']);
}
}
usort($groups, 'local_cmp');
}
return $groups;
}
/**
* Get the group's cn by giving his DN
*
* @param resource $ldap_connection ldap connection to use
* @param string $group_dn the group's dn
*
* @return string the group cn
*/
static function getGroupCNByDn($ldap_connection, $group_dn) {
$sr = @ ldap_read($ldap_connection, $group_dn, "objectClass=*", ["cn"]);
if ($sr === false) {
//group does not exists
return false;
}
$v = self::get_entries_clean($ldap_connection, $sr);
if (!is_array($v) || (count($v) == 0) || empty($v[0]["cn"][0])) {
return false;
}
return $v[0]["cn"][0];
}
/**
* Set groups from ldap
*
* @since 0.84 new parameter $limitexceeded
*
* @param resource $ldap_connection LDAP connection
* @param object $config_ldap LDAP configuration
* @param string $filter Filters
* @param boolean $limitexceeded Is limit exceeded
* @param boolean $search_in_groups Search in groups (true by default)
* @param array $groups Groups to search
*
* @return array
*/
static function getGroupsFromLDAP($ldap_connection, $config_ldap, $filter,
&$limitexceeded, $search_in_groups = true,
$groups = []) {
global $DB;
//First look for groups in group objects
$extra_attribute = ($search_in_groups?"cn":$config_ldap->fields["group_field"]);
$attrs = ["dn", $extra_attribute];
if ($filter == '') {
if ($search_in_groups) {
$filter = (!empty($config_ldap->fields['group_condition'])
? $config_ldap->fields['group_condition'] : "(objectclass=*)");
} else {
$filter = (!empty($config_ldap->fields['condition'])
? $config_ldap->fields['condition'] : "(objectclass=*)");
}
}
$cookie = '';
$count = 0;
do {
if (self::isLdapPageSizeAvailable($config_ldap)) {
ldap_control_paged_result($ldap_connection, $config_ldap->fields['pagesize'],
true, $cookie);
}
$filter = Toolbox::unclean_cross_side_scripting_deep($filter);
$sr = @ldap_search($ldap_connection, $config_ldap->fields['basedn'], $filter,
$attrs);
if ($sr) {
if (in_array(ldap_errno($ldap_connection), [4,11])) {
// openldap return 4 for Size limit exceeded
$limitexceeded = true;
}
$infos = self::get_entries_clean($ldap_connection, $sr);
if (in_array(ldap_errno($ldap_connection), [4,11])) {
// openldap return 4 for Size limit exceeded
$limitexceeded = true;
}
$count += $infos['count'];
//If page results are enabled and the number of results is greater than the maximum allowed
//warn user that limit is exceeded and stop search
if (self::isLdapPageSizeAvailable($config_ldap)
&& $config_ldap->fields['ldap_maxlimit']
&& ($count > $config_ldap->fields['ldap_maxlimit'])) {
$limitexceeded = true;
break;
}
for ($ligne=0; $ligne < $infos["count"]; $ligne++) {
if ($search_in_groups) {
// No cn : not a real object
if (isset($infos[$ligne]["cn"][0])) {
$groups[$infos[$ligne]["dn"]] = (["cn" => $infos[$ligne]["cn"][0],
"search_type" => "groups"]);
}
} else {
if (isset($infos[$ligne][$extra_attribute])) {
if (($config_ldap->fields["group_field"] == 'dn')
|| in_array('ou', $groups)) {
$dn = $infos[$ligne][$extra_attribute];
$ou = [];
for ($tmp=$dn; count($tmptab = explode(',', $tmp, 2))==2; $tmp=$tmptab[1]) {
$ou[] = $tmptab[1];
}
/// Search in DB for group with ldap_group_dn
if (($config_ldap->fields["group_field"] == 'dn')
&& (count($ou) > 0)) {
$iterator = $DB->request([
'SELECT' => ['ldap_value'],
'FROM' => 'glpi_groups',
'WHERE' => [
'ldap_group_dn' => Toolbox::addslashes_deep($ou)
]
]);
while ($group = $iterator->next()) {
$groups[$group['ldap_value']] = ["cn" => $group['ldap_value'],
"search_type" => "users"];
}
}
} else {
for ($ligne_extra=0; $ligne_extra<$infos[$ligne][$extra_attribute]["count"];
$ligne_extra++) {
$groups[$infos[$ligne][$extra_attribute][$ligne_extra]]
= ["cn" => self::getGroupCNByDn($ldap_connection,
$infos[$ligne][$extra_attribute][$ligne_extra]),
"search_type"
=> "users"];
}
}
}
}
}
}
if (self::isLdapPageSizeAvailable($config_ldap)) {
ldap_control_paged_result_response($ldap_connection, $sr, $cookie);
}
} while (($cookie !== null) && ($cookie != ''));
return $groups;
}
/**
* Form to choose a ldap server
*
* @param string $target target page for the form
*
* @return void
*/
static function ldapChooseDirectory($target) {
global $DB;
$iterator = $DB->request([
'FROM' => self::getTable(),
'WHERE' => [
'is_active' => 1
],
'ORDER' => 'name ASC'
]);
if (count($iterator) == 1) {
//If only one server, do not show the choose ldap server window
$ldap = $iterator->next();
$_SESSION["ldap_server"] = $ldap["id"];
Html::redirect($_SERVER['PHP_SELF']);
}
echo "";
}
/**
* Force synchronization for one user
*
* @param User $user User to synchronize
* @param boolean $display Display message information on redirect (true by default)
*
* @return array|boolean with state, else false
*/
static function forceOneUserSynchronization(User $user, $display = true) {
$authldap = new AuthLdap();
//Get the LDAP server from which the user has been imported
if ($authldap->getFromDB($user->fields['auths_id'])) {
$user_field = 'name';
$id_field = $authldap->fields['login_field'];
if ($authldap->isSyncFieldEnabled() && !empty($user->fields['sync_field'])) {
$user_field = 'sync_field';
$id_field = $authldap->fields['sync_field'];
}
return AuthLdap::ldapImportUserByServerId(
[
'method' => self::IDENTIFIER_LOGIN,
'value' => $user->fields[$user_field],
'identifier_field' => $id_field,
'user_field' => $user_field
],
true,
$user->fields["auths_id"],
$display
);
}
return false;
}
/**
* Import a user from a specific ldap server
*
* @param array $params of parameters: method (IDENTIFIER_LOGIN or IDENTIFIER_EMAIL) + value
* @param boolean $action synchoronize (true) or import (false)
* @param integer $ldap_server ID of the LDAP server to use
* @param boolean $display display message information on redirect (false by default)
*
* @return array|boolean with state, else false
*/
static function ldapImportUserByServerId(array $params, $action, $ldap_server,
$display = false) {
$params = Toolbox::stripslashes_deep($params);
$config_ldap = new self();
$res = $config_ldap->getFromDB($ldap_server);
$input = [];
// we prevent some delay...
if (!$res) {
return false;
}
if (!isset($params['identifier_field'])) {
$params['identifier_field'] = $config_ldap->getLdapIdentifierToUse();
}
if (!isset($params['user_field'])) {
$params['user_field'] = $config_ldap->getDatabaseIdentifierToUse();
}
$search_parameters = [];
//Connect to the directory
if (isset(self::$conn_cache[$ldap_server])) {
$ds = self::$conn_cache[$ldap_server];
} else {
$ds = $config_ldap->connect();
}
if ($ds) {
self::$conn_cache[$ldap_server] = $ds;
$search_parameters['method'] = $params['method'];
$search_parameters['fields'][self::IDENTIFIER_LOGIN] = $params['identifier_field'];
if ($params['method'] == self::IDENTIFIER_EMAIL) {
$search_parameters['fields'][self::IDENTIFIER_EMAIL]
= $config_ldap->fields['email1_field'];
}
//Get the user's dn & login
$attribs = ['basedn' => $config_ldap->fields['basedn'],
'login_field' => $search_parameters['fields'][$search_parameters['method']],
'search_parameters' => $search_parameters,
'user_params' => $params,
'condition' => $config_ldap->fields['condition']];
try {
$infos = self::searchUserDn($ds, $attribs);
$login = self::getFieldValue($infos, $search_parameters['fields'][$search_parameters['method']]);
if ($infos && $infos['dn']) {
$user_dn = $infos['dn'];
$user = new User();
//Get information from LDAP
if ($user->getFromLDAP($ds, $config_ldap->fields, $user_dn, addslashes($login),
($action == self::ACTION_IMPORT))) {
// Add the auth method
// Force date sync
$user->fields["date_sync"] = $_SESSION["glpi_currenttime"];
$user->fields['is_deleted_ldap'] = 0;
//Save information in database !
$input = $user->fields;
//clean picture from input
// (picture managed in User::post_addItem and prepareInputForUpdate)
unset($input['picture']);
if ($action == self::ACTION_IMPORT) {
$input["authtype"] = Auth::LDAP;
$input["auths_id"] = $ldap_server;
// Display message after redirect
if ($display) {
$input['add'] = 1;
}
$user->fields["id"] = $user->add($input);
return ['action' => self::USER_IMPORTED,
'id' => $user->fields["id"]];
}
//Get the ID by user name
if (!($id = User::getIdByfield($params['user_field'], $login))) {
//In case user id as changed : get id by dn
$id = User::getIdByfield('user_dn', $user_dn);
}
$input['id'] = $id;
if ($display) {
$input['update'] = 1;
}
$user->update($input);
return ['action' => self::USER_SYNCHRONIZED,
'id' => $input['id']];
}
return false;
}
if ($action != self::ACTION_IMPORT) {
$users_id = User::getIdByField($params['user_field'], $params['value']);
User::manageDeletedUserInLdap($users_id);
return ['action' => self::USER_DELETED_LDAP,
'id' => $users_id];
}
} catch (\RuntimeException $e) {
Toolbox::logError($e->getMessage());
return false;
}
} else {
return false;
}
}
/**
* Import grousp from an LDAP directory
*
* @param string $group_dn dn of the group to import
* @param array $options array for
* - authldaps_id
* - entities_id where group must to be imported
* - is_recursive
*
* @return integer|false
*/
static function ldapImportGroup($group_dn, $options = []) {
$config_ldap = new self();
$res = $config_ldap->getFromDB($options['authldaps_id']);
// we prevent some delay...
if (!$res) {
return false;
}
//Connect to the directory
$ds = $config_ldap->connect();
if ($ds) {
$group_infos = self::getGroupByDn($ds, stripslashes($group_dn));
$group = new Group();
if ($options['type'] == "groups") {
return $group->add(["name" => addslashes($group_infos["cn"][0]),
"ldap_group_dn" => addslashes($group_infos["dn"]),
"entities_id" => $options['entities_id'],
"is_recursive" => $options['is_recursive']]);
}
return $group->add(["name" => addslashes($group_infos["cn"][0]),
"ldap_field" => $config_ldap->fields["group_field"],
"ldap_value" => addslashes($group_infos["dn"]),
"entities_id" => $options['entities_id'],
"is_recursive" => $options['is_recursive']]);
}
return false;
}
/**
* Open LDAP connection to current server
*
* @return resource|boolean
*/
function connect() {
return $this->connectToServer($this->fields['host'], $this->fields['port'],
$this->fields['rootdn'],
Toolbox::decrypt($this->fields['rootdn_passwd'], GLPIKEY),
$this->fields['use_tls'],
$this->fields['deref_option']);
}
/**
* Connect to a LDAP server
*
* @param string $host LDAP host to connect
* @param string $port port to use
* @param string $login login to use (default '')
* @param string $password password to use (default '')
* @param boolean $use_tls use a TLS connection? (false by default)
* @param integer $deref_options deref options used
*
* @return resource link to the LDAP server : false if connection failed
*/
static function connectToServer($host, $port, $login = "", $password = "",
$use_tls = false, $deref_options = 0) {
$ds = @ldap_connect($host, intval($port));
if ($ds) {
@ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
@ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
@ldap_set_option($ds, LDAP_OPT_DEREF, $deref_options);
if ($use_tls) {
if (!@ldap_start_tls($ds)) {
return false;
}
}
// Auth bind
if ($login != '') {
$b = @ldap_bind($ds, $login, $password);
} else { // Anonymous bind
$b = @ldap_bind($ds);
}
if ($b) {
return $ds;
}
}
return false;
}
/**
* Try to connect to a ldap server
*
* @param array $ldap_method ldap_method array to use
* @param string $login User Login
* @param string $password User Password
*
* @return resource|boolean link to the LDAP server : false if connection failed
*/
static function tryToConnectToServer($ldap_method, $login, $password) {
if (!function_exists('ldap_connect')) {
Toolbox::logError("ldap_connect function is missing. Did you miss install php-ldap extension?");
return false;
}
$ds = self::connectToServer($ldap_method['host'], $ldap_method['port'],
$ldap_method['rootdn'],
Toolbox::decrypt($ldap_method['rootdn_passwd'], GLPIKEY),
$ldap_method['use_tls'], $ldap_method['deref_option']);
// Test with login and password of the user if exists
if (!$ds
&& !empty($login)) {
$ds = self::connectToServer($ldap_method['host'], $ldap_method['port'], $login,
$password, $ldap_method['use_tls'],
$ldap_method['deref_option']);
}
//If connection is not successfull on this directory, try replicates (if replicates exists)
if (!$ds
&& ($ldap_method['id'] > 0)) {
foreach (self::getAllReplicateForAMaster($ldap_method['id']) as $replicate) {
$ds = self::connectToServer($replicate["host"], $replicate["port"],
$ldap_method['rootdn'],
Toolbox::decrypt($ldap_method['rootdn_passwd'], GLPIKEY),
$ldap_method['use_tls'], $ldap_method['deref_option']);
// Test with login and password of the user
if (!$ds
&& !empty($login)) {
$ds = self::connectToServer($replicate["host"], $replicate["port"], $login,
$password, $ldap_method['use_tls'],
$ldap_method['deref_option']);
}
if ($ds) {
return $ds;
}
}
}
return $ds;
}
/**
* Get LDAP servers
*
* @return array
*/
static function getLdapServers() {
return getAllDatasFromTable('glpi_authldaps', [], false, '`is_default` DESC');
}
/**
* Is the LDAP authentication used?
*
* @return boolean
*/
static function useAuthLdap() {
return (countElementsInTable('glpi_authldaps', ['is_active' => 1]) > 0);
}
/**
* Import a user from ldap
* Check all the directories. When the user is found, then import it
*
* @param array $options array containing condition:
* array('name'=>'glpi') or array('email' => 'test at test.com')
*
* @return array|boolean false if fail
*/
static function importUserFromServers($options = []) {
$auth = new Auth();
$params = [];
if (isset($options['name'])) {
$params['value'] = $options['name'];
$params['method'] = self::IDENTIFIER_LOGIN;
}
if (isset($options['email'])) {
$params['value'] = $options['email'];
$params['method'] = self::IDENTIFIER_EMAIL;
}
$auth->user_present = $auth->userExists($options);
//If the user does not exists
if ($auth->user_present == 0) {
$auth->getAuthMethods();
$ldap_methods = $auth->authtypes["ldap"];
foreach ($ldap_methods as $ldap_method) {
if ($ldap_method['is_active']) {
//we're looking for a user login
$params['identifier_field'] = $ldap_method['login_field'];
$params['user_field'] = 'name';
$result = self::ldapImportUserByServerId($params, 0, $ldap_method["id"], true);
if ($result != false) {
return $result;
}
}
}
Session::addMessageAfterRedirect(__('User not found or several users found'), false, ERROR);
} else {
Session::addMessageAfterRedirect(__('Unable to add. The user already exist.'), false,
ERROR);
}
return false;
}
/**
* Authentify a user by checking a specific directory
*
* @param object $auth identification object
* @param string $login user login
* @param string $password user password
* @param array $ldap_method ldap_method array to use
* @param string $user_dn user LDAP DN if present
*
* @return object identification object
*/
static function ldapAuth($auth, $login, $password, $ldap_method, $user_dn) {
$oldlevel = error_reporting(0);
$infos = $auth->connection_ldap($ldap_method, $login, $password);
$user_dn = $infos['dn'];
$user_sync = (isset($infos['sync_field']) ? $infos['sync_field'] : null);
error_reporting($oldlevel);
$auth->auth_succeded = false;
$auth->extauth = 1;
if ($user_dn) {
$auth->auth_succeded = true;
// try by login+auth_id and next by dn
if ($auth->user->getFromDBbyNameAndAuth($login, Auth::LDAP, $ldap_method['id'])
|| $auth->user->getFromDBbyDn(toolbox::addslashes_deep($user_dn))) {
//There's already an existing user in DB with the same DN but its login field has changed
$auth->user->fields['name'] = $login;
$auth->user_present = true;
$auth->user_dn = $user_dn;
} else if ($user_sync !== null && $auth->user->getFromDBbySyncField($user_sync)) {
//user login/dn have changed
$auth->user->fields['name'] = $login;
$auth->user->fields['user_dn'] = $user_dn;
$auth->user_present = true;
$auth->user_dn = $user_dn;
} else { // The user is a new user
$auth->user_present = false;
}
$auth->user->getFromLDAP($auth->ldap_connection, $ldap_method, $user_dn, $login,
!$auth->user_present);
$auth->user->fields["authtype"] = Auth::LDAP;
$auth->user->fields["auths_id"] = $ldap_method["id"];
}
return $auth;
}
/**
* Try to authentify a user by checking all the directories
*
* @param object $auth identification object
* @param string $login user login
* @param string $password user password
* @param integer $auths_id auths_id already used for the user (default 0)
* @param boolean $user_dn user LDAP DN if present (false by default)
* @param boolean $break if user is not found in the first directory,
* continue searching on the following ones (true by default)
*
* @return object identification object
*/
static function tryLdapAuth($auth, $login, $password, $auths_id = 0, $user_dn = false, $break = true) {
//If no specific source is given, test all ldap directories
if ($auths_id <= 0) {
foreach ($auth->authtypes["ldap"] as $ldap_method) {
if ($ldap_method['is_active']) {
$auth = self::ldapAuth($auth, $login, $password, $ldap_method, $user_dn);
if ($auth->auth_succeded
&& $break) {
break;
}
}
}
} else if (array_key_exists($auths_id, $auth->authtypes["ldap"])) {
// Check if the ldap server indicated as the last good one still exists !
//A specific ldap directory is given, test it and only this one !
$auth = self::ldapAuth($auth, $login, $password, $auth->authtypes["ldap"][$auths_id],
$user_dn);
}
return $auth;
}
/**
* Get dn for a user
*
* @param resource $ds LDAP link
* @param array $options array of possible options:
* - basedn : base dn used to search
* - login_field : attribute to store login
* - search_parameters array of search parameters
* - user_params array of parameters : method (IDENTIFIER_LOGIN or IDENTIFIER_EMAIL) + value
* - condition : ldap condition used
*
* @return array|boolean dn of the user, else false
* @throws \RuntimeException
*/
static function searchUserDn($ds, $options = []) {
$values = [
'basedn' => '',
'login_field' => '',
'search_parameters' => [],
'user_params' => '',
'condition' => '',
'user_dn' => false,
];
foreach ($options as $key => $value) {
$values[$key] = $value;
}
//By default authentify users by login
//$authentification_value = '';
$login_attr = $values['search_parameters']['fields'][self::IDENTIFIER_LOGIN];
$sync_attr = (isset($values['search_parameters']['fields']['sync_field'])) ?
$values['search_parameters']['fields']['sync_field'] : null;
$ldap_parameters = ["dn"];
foreach ($values['search_parameters']['fields'] as $parameter) {
$ldap_parameters[] = $parameter;
}
//First : if a user dn is provided, look for it in the directory
//Before trying to find the user using his login_field
if ($values['user_dn']) {
$info = self::getUserByDn($ds, $values['user_dn'], $ldap_parameters);
if ($info) {
$ret = [
'dn' => $values['user_dn'],
$login_attr => $info[$login_attr][0]
];
if ($sync_attr !== null && isset($info[0][$sync_attr])) {
$ret['sync_field'] = self::getFieldValue($info[0], $sync_attr);
}
return $ret;
}
}
//$authentification_value = $values['user_params']['value'];
// Tenter une recherche pour essayer de retrouver le DN
$filter_value = $values['user_params']['value'];
if ($values['login_field'] == 'objectguid' && self::isValidGuid($filter_value)) {
$filter_value = self::guidToHex($filter_value);
}
$filter = "(".$values['login_field']."=".$filter_value.")";
if (!empty($values['condition'])) {
$filter = "(& $filter ".$values['condition'].")";
}
if ($result = @ldap_search($ds, $values['basedn'], $filter, $ldap_parameters)) {
//search has been done, let's check for found results
$info = self::get_entries_clean($ds, $result);
if (is_array($info) && ($info['count'] == 1)) {
$ret = [
'dn' => $info[0]['dn'],
$login_attr => $info[0][$login_attr][0]
];
if ($sync_attr !== null && isset($info[0][$sync_attr])) {
$ret['sync_field'] = self::getFieldValue($info[0], $sync_attr);
}
return $ret;
}
return false;
}
throw new \RuntimeException('Something went wrong searching in LDAP directory');
}
/**
* Get an object from LDAP by giving his DN
*
* @param resource $ds the active connection to the directory
* @param string $condition the LDAP filter to use for the search
* @param string $dn DN of the object
* @param array $attrs of the attributes to retreive
* @param boolean $clean (true by default)
*
* @return array|boolean false if failed
*/
static function getObjectByDn($ds, $condition, $dn, $attrs = [], $clean = true) {
if ($result = @ ldap_read($ds, $dn, $condition, $attrs)) {
if ($clean) {
$info = self::get_entries_clean($ds, $result);
} else {
$info = ldap_get_entries($ds, $result);
}
if (is_array($info) && ($info['count'] == 1)) {
return $info[0];
}
}
return false;
}
/**
* Get user by domain name
*
* @param resource $ds the active connection to the directory
* @param string $user_dn domain name
* @param array $attrs attributes
* @param boolean $clean (true by default)
*
* @return array|boolean false if failed
*/
static function getUserByDn($ds, $user_dn, $attrs, $clean = true) {
return self::getObjectByDn($ds, "objectClass=*", $user_dn, $attrs, $clean);
}
/**
* Get infos for groups
*
* @param resource $ds LDAP link
* @param string $group_dn dn of the group
*
* @return array|boolean group infos if found, else false
*/
static function getGroupByDn($ds, $group_dn) {
return self::getObjectByDn($ds, "objectClass=*", $group_dn, ["cn"]);
}
/**
* Manage values stored in session
*
* @param array $options Options
* @param boolean $delete (false by default)
*
* @return void
*/
static function manageValuesInSession($options = [], $delete = false) {
$fields = ['action', 'authldaps_id', 'basedn', 'begin_date', 'criterias', 'end_date',
'entities_id', 'interface', 'ldap_filter', 'mode'];
//If form accessed via modal, do not show expert mode link
// Manage new value is set : entity or mode
if (isset($options['entity'])
|| isset($options['mode'])) {
if (isset($options['_in_modal']) && $options['_in_modal']) {
//If coming form the helpdesk form : reset all criterias
$_SESSION['ldap_import']['_in_modal'] = 1;
$_SESSION['ldap_import']['no_expert_mode'] = 1;
$_SESSION['ldap_import']['action'] = 'show';
$_SESSION['ldap_import']['interface'] = self::SIMPLE_INTERFACE;
$_SESSION['ldap_import']['mode'] = self::ACTION_IMPORT;
} else {
$_SESSION['ldap_import']['_in_modal'] = 0;
$_SESSION['ldap_import']['no_expert_mode'] = 0;
}
}
if (!$delete) {
if (!isset($_SESSION['ldap_import']['entities_id'])) {
$options['entities_id'] = $_SESSION['glpiactive_entity'];
}
if (isset($options['toprocess'])) {
$_SESSION['ldap_import']['action'] = 'process';
}
if (isset($options['change_directory'])) {
$options['ldap_filter'] = '';
}
if (!isset($_SESSION['ldap_import']['authldaps_id'])) {
$_SESSION['ldap_import']['authldaps_id'] = NOT_AVAILABLE;
}
if ((!Config::canUpdate()
&& !Entity::canUpdate())
|| (!isset($_SESSION['ldap_import']['interface'])
&& !isset($options['interface']))) {
$options['interface'] = self::SIMPLE_INTERFACE;
}
foreach ($fields as $field) {
if (isset($options[$field])) {
$_SESSION['ldap_import'][$field] = $options[$field];
}
}
if (isset($_SESSION['ldap_import']['begin_date'])
&& ($_SESSION['ldap_import']['begin_date'] == 'NULL')) {
$_SESSION['ldap_import']['begin_date'] = '';
}
if (isset($_SESSION['ldap_import']['end_date'])
&& ($_SESSION['ldap_import']['end_date'] == 'NULL')) {
$_SESSION['ldap_import']['end_date'] = '';
}
if (!isset($_SESSION['ldap_import']['criterias'])) {
$_SESSION['ldap_import']['criterias'] = [];
}
$authldap = new self();
//Filter computation
if ($_SESSION['ldap_import']['interface'] == self::SIMPLE_INTERFACE) {
$entity = new Entity();
if ($entity->getFromDB($_SESSION['ldap_import']['entities_id'])
&& ($entity->getField('authldaps_id') > 0)) {
$authldap->getFromDB($_SESSION['ldap_import']['authldaps_id']);
$_SESSION['ldap_import']['authldaps_id'] = $entity->getField('authldaps_id');
$_SESSION['ldap_import']['basedn'] = $entity->getField('ldap_dn');
// No dn specified in entity : use standard one
if (empty($_SESSION['ldap_import']['basedn'])) {
$_SESSION['ldap_import']['basedn'] = $authldap->getField('basedn');
}
if ($entity->getField('entity_ldapfilter') != NOT_AVAILABLE) {
$_SESSION['ldap_import']['entity_filter']
= $entity->getField('entity_ldapfilter');
}
} else {
if ($_SESSION['ldap_import']['authldaps_id'] == NOT_AVAILABLE
|| !$_SESSION['ldap_import']['authldaps_id']) {
$_SESSION['ldap_import']['authldaps_id'] = self::getDefault();
}
if ($_SESSION['ldap_import']['authldaps_id'] > 0) {
$authldap->getFromDB($_SESSION['ldap_import']['authldaps_id']);
$_SESSION['ldap_import']['basedn'] = $authldap->getField('basedn');
}
}
if ($_SESSION['ldap_import']['authldaps_id'] > 0) {
$_SESSION['ldap_import']['ldap_filter'] = self::buildLdapFilter($authldap);
}
} else {
if ($_SESSION['ldap_import']['authldaps_id'] == NOT_AVAILABLE
|| !$_SESSION['ldap_import']['authldaps_id']) {
$_SESSION['ldap_import']['authldaps_id'] = self::getDefault();
if ($_SESSION['ldap_import']['authldaps_id'] > 0) {
$authldap->getFromDB($_SESSION['ldap_import']['authldaps_id']);
$_SESSION['ldap_import']['basedn'] = $authldap->getField('basedn');
}
}
if (!isset($_SESSION['ldap_import']['ldap_filter'])
|| $_SESSION['ldap_import']['ldap_filter'] == '') {
$authldap->getFromDB($_SESSION['ldap_import']['authldaps_id']);
$_SESSION['ldap_import']['basedn'] = $authldap->getField('basedn');
$_SESSION['ldap_import']['ldap_filter'] = self::buildLdapFilter($authldap);
}
}
} else { // Unset all values in session
unset($_SESSION['ldap_import']);
}
}
/**
* Show import user form
*
* @param object $authldap AuthLDAP object
*
* @return void
*/
static function showUserImportForm(AuthLDAP $authldap) {
//Get data related to entity (directory and ldap filter)
$authldap->getFromDB($_SESSION['ldap_import']['authldaps_id']);
echo "