ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilLDAPQuery.php
Go to the documentation of this file.
1 <?php
2 /*
3  +-----------------------------------------------------------------------------+
4  | ILIAS open source |
5  +-----------------------------------------------------------------------------+
6  | Copyright (c) 1998-2006 ILIAS open source, University of Cologne |
7  | |
8  | This program is free software; you can redistribute it and/or |
9  | modify it under the terms of the GNU General Public License |
10  | as published by the Free Software Foundation; either version 2 |
11  | of the License, or (at your option) any later version. |
12  | |
13  | This program is distributed in the hope that it will be useful, |
14  | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16  | GNU General Public License for more details. |
17  | |
18  | You should have received a copy of the GNU General Public License |
19  | along with this program; if not, write to the Free Software |
20  | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21  +-----------------------------------------------------------------------------+
22 */
23 
24 define('IL_LDAP_BIND_DEFAULT', 0);
25 define('IL_LDAP_BIND_ADMIN', 1);
26 define('IL_LDAP_BIND_TEST', 2);
27 define('IL_LDAP_BIND_AUTH', 10);
28 
29 include_once('Services/LDAP/classes/class.ilLDAPAttributeMapping.php');
30 include_once('Services/LDAP/classes/class.ilLDAPResult.php');
31 include_once('Services/LDAP/classes/class.ilLDAPQueryException.php');
32 
43 {
44  private $ldap_server_url = null;
45  private $settings = null;
46 
50  private $log = null;
51 
52  private $user_fields = array();
53 
62  public function __construct(ilLDAPServer $a_server, $a_url = '')
63  {
64  $this->settings = $a_server;
65 
66  if (strlen($a_url)) {
67  $this->ldap_server_url = $a_url;
68  } else {
69  $this->ldap_server_url = $this->settings->getUrl();
70  }
71 
72  $this->mapping = ilLDAPAttributeMapping::_getInstanceByServerId($this->settings->getServerId());
73  $this->log = $GLOBALS['DIC']->logger()->auth();
74 
75  $this->fetchUserProfileFields();
76  $this->connect();
77  }
78 
79  // begin-patch ldap_multiple
84  public function getServer()
85  {
86  return $this->settings;
87  }
88 
93  public function getLogger()
94  {
95  return $this->log;
96  }
97 
105  public function fetchUser($a_name)
106  {
107  if (!$this->readUserData($a_name)) {
108  return array();
109  } else {
110  return $this->users;
111  }
112  }
113 
114 
121  public function fetchUsers()
122  {
123  // First of all check if a group restriction is enabled
124  // YES: => fetch all group members
125  // No: => fetch all users
126  if (strlen($this->settings->getGroupName())) {
127  $this->log->debug('Searching for group members.');
128 
129  $groups = $this->settings->getGroupNames();
130  if (count($groups) <= 1) {
131  $this->fetchGroupMembers();
132  } else {
133  foreach ($groups as $group) {
134  $this->fetchGroupMembers($group);
135  }
136  }
137  }
138  if (!strlen($this->settings->getGroupName()) or $this->settings->isMembershipOptional()) {
139  $this->log->info('Start reading all users...');
140  $this->readAllUsers();
141  #throw new ilLDAPQueryException('LDAP: Called import of users without specifying group restrictions. NOT IMPLEMENTED YET!');
142  }
143  return $this->users ? $this->users : array();
144  }
145 
157  public function query($a_search_base, $a_filter, $a_scope, $a_attributes)
158  {
159  $res = $this->queryByScope($a_scope, $a_search_base, $a_filter, $a_attributes);
160  if ($res === false) {
161  throw new ilLDAPQueryException(__METHOD__ . ' ' . ldap_error($this->lh) . ' ' .
162  sprintf(
163  'DN: %s, Filter: %s, Scope: %s',
164  $a_search_base,
165  $a_filter,
166  $a_scope
167  ));
168  }
169  return new ilLDAPResult($this->lh, $res);
170  }
171 
178  public function modAdd($a_dn, $a_attribute)
179  {
180  if (@ldap_mod_add($this->lh, $a_dn, $a_attribute)) {
181  return true;
182  }
183  throw new ilLDAPQueryException(__METHOD__ . ' ' . ldap_error($this->lh));
184  }
185 
192  public function modDelete($a_dn, $a_attribute)
193  {
194  if (@ldap_mod_del($this->lh, $a_dn, $a_attribute)) {
195  return true;
196  }
197  throw new ilLDAPQueryException(__METHOD__ . ' ' . ldap_error($this->lh));
198  }
199 
208  private function readAllUsers()
209  {
210  // Build search base
211  if (($dn = $this->settings->getSearchBase()) && substr($dn, -1) != ',') {
212  $dn .= ',';
213  }
214  $dn .= $this->settings->getBaseDN();
215 
216  // page results
217  $filter = $this->settings->getFilter();
218  $page_filter = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','-');
219  $chars = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');
220 
221  foreach ($page_filter as $letter) {
222  $new_filter = '(&';
223  $new_filter .= $filter;
224 
225  switch ($letter) {
226  case '-':
227  $new_filter .= ('(!(|');
228  foreach ($chars as $char) {
229  $new_filter .= ('(' . $this->settings->getUserAttribute() . '=' . $char . '*)');
230  }
231  $new_filter .= ')))';
232  break;
233 
234  default:
235  $new_filter .= ('(' . $this->settings->getUserAttribute() . '=' . $letter . '*))');
236  break;
237  }
238 
239  $this->log->info('Searching with ldap search and filter ' . $new_filter . ' in ' . $dn);
240  $res = $this->queryByScope(
241  $this->settings->getUserScope(),
242  $dn,
243  $new_filter,
244  array($this->settings->getUserAttribute())
245  );
246 
247  $tmp_result = new ilLDAPResult($this->lh, $res);
248  if (!$tmp_result->numRows()) {
249  $this->log->notice('No users found. Aborting.');
250  continue;
251  }
252  $this->log->info('Found ' . $tmp_result->numRows() . ' users.');
253  $attribute = strtolower($this->settings->getUserAttribute());
254  foreach ($tmp_result->getRows() as $data) {
255  if (isset($data[$attribute])) {
256  $this->readUserData($data[$attribute], false, false);
257  } else {
258  $this->log->warning('Unknown error. No user attribute found.');
259  }
260  }
261  unset($tmp_result);
262  }
263  return true;
264  }
265 
272  public function checkGroupMembership($a_ldap_user_name, $ldap_user_data)
273  {
274  $group_names = $this->getServer()->getGroupNames();
275 
276  if (!count($group_names)) {
277  $this->getLogger()->debug('No LDAP group restrictions found');
278  return true;
279  }
280 
281  $group_dn = $this->getServer()->getGroupDN();
282  if (
283  $group_dn &&
284  (substr($group_dn, -1) != ',')
285  ) {
286  $group_dn .= ',';
287  }
288  $group_dn .= $this->getServer()->getBaseDN();
289 
290  foreach ($group_names as $group) {
291  $user = $a_ldap_user_name;
292  if ($this->getServer()->enabledGroupMemberIsDN()) {
293  if ($this->getServer()->enabledEscapeDN()) {
294  $user = ldap_escape($ldap_user_data['dn'], "", LDAP_ESCAPE_FILTER);
295  } else {
296  $user = $ldap_user_data['dn'];
297  }
298  }
299 
300  $filter = sprintf(
301  '(&(%s=%s)(%s=%s)%s)',
302  $this->getServer()->getGroupAttribute(),
303  $group,
304  $this->getServer()->getGroupMember(),
305  $user,
306  $this->getServer()->getGroupFilter()
307  );
308  $this->getLogger()->debug('Current group search base: ' . $group_dn);
309  $this->getLogger()->debug('Current group filter: ' . $filter);
310 
311  $res = $this->queryByScope(
312  $this->getServer()->getGroupScope(),
313  $group_dn,
314  $filter,
315  [$this->getServer()->getGroupMember()]
316  );
317 
318  $this->getLogger()->dump($res);
319 
320  $tmp_result = new ilLDAPResult($this->lh, $res);
321  $group_result = $tmp_result->getRows();
322 
323  $this->getLogger()->debug('Group query returned: ');
324  $this->getLogger()->dump($group_result, ilLogLevel::DEBUG);
325 
326  if (count($group_result)) {
327  return true;
328  }
329  }
330 
331  // group restrictions failed check optional membership
332  if ($this->getServer()->isMembershipOptional()) {
333  $this->getLogger()->debug('Group restrictions failed, checking user filter.');
334  if ($this->readUserData($a_ldap_user_name, true, true)) {
335  $this->getLogger()->debug('User filter matches.');
336  return true;
337  }
338  }
339  $this->getLogger()->debug('Group restrictions failed.');
340  return false;
341  }
342 
343 
350  private function fetchGroupMembers($a_name = '')
351  {
352  $group_name = strlen($a_name) ? $a_name : $this->settings->getGroupName();
353 
354  // Build filter
355  $filter = sprintf(
356  '(&(%s=%s)%s)',
357  $this->settings->getGroupAttribute(),
358  $group_name,
359  $this->settings->getGroupFilter()
360  );
361 
362 
363  // Build search base
364  if (($gdn = $this->settings->getGroupDN()) && substr($gdn, -1) != ',') {
365  $gdn .= ',';
366  }
367  $gdn .= $this->settings->getBaseDN();
368 
369  $this->log->debug('Using filter ' . $filter);
370  $this->log->debug('Using DN ' . $gdn);
371  $res = $this->queryByScope(
372  $this->settings->getGroupScope(),
373  $gdn,
374  $filter,
375  array($this->settings->getGroupMember())
376  );
377 
378  $tmp_result = new ilLDAPResult($this->lh, $res);
379  $group_data = $tmp_result->getRows();
380 
381 
382  if (!$tmp_result->numRows()) {
383  $this->log->info('No group found.');
384  return false;
385  }
386 
387  $attribute_name = strtolower($this->settings->getGroupMember());
388 
389  // All groups
390  foreach ($group_data as $data) {
391  if (is_array($data[$attribute_name])) {
392  $this->log->debug('Found ' . count($data[$attribute_name]) . ' group members for group ' . $data['dn']);
393  foreach ($data[$attribute_name] as $name) {
394  $this->readUserData($name, true, true);
395  }
396  } else {
397  $this->readUserData($data[$attribute_name], true, true);
398  }
399  }
400  unset($tmp_result);
401  return;
402  }
403 
410  private function readUserData($a_name, $a_check_dn = false, $a_try_group_user_filter = false)
411  {
412  $filter = $this->settings->getFilter();
413  if ($a_try_group_user_filter) {
414  if ($this->settings->isMembershipOptional()) {
415  $filter = $this->settings->getGroupUserFilter();
416  }
417  }
418 
419  // Build filter
420  if ($this->settings->enabledGroupMemberIsDN() and $a_check_dn) {
421  $dn = $a_name;
422  #$res = $this->queryByScope(IL_LDAP_SCOPE_BASE,$dn,$filter,$this->user_fields);
423 
424  $fields = array_merge($this->user_fields, array('useraccountcontrol'));
425  $res = $this->queryByScope(IL_LDAP_SCOPE_BASE, strtolower($dn), $filter, $fields);
426  } else {
427  $filter = sprintf(
428  '(&(%s=%s)%s)',
429  $this->settings->getUserAttribute(),
430  $a_name,
431  $filter
432  );
433 
434  // Build search base
435  if (($dn = $this->settings->getSearchBase()) && substr($dn, -1) != ',') {
436  $dn .= ',';
437  }
438  $dn .= $this->settings->getBaseDN();
439  $fields = array_merge($this->user_fields, array('useraccountcontrol'));
440  $res = $this->queryByScope($this->settings->getUserScope(), strtolower($dn), $filter, $fields);
441  }
442 
443 
444  $tmp_result = new ilLDAPResult($this->lh, $res);
445  if (!$tmp_result->numRows()) {
446  $this->log->info('LDAP: No user data found for: ' . $a_name);
447  unset($tmp_result);
448  return false;
449  }
450 
451  if ($user_data = $tmp_result->get()) {
452  if (isset($user_data['useraccountcontrol'])) {
453  if (($user_data['useraccountcontrol'] & 0x02)) {
454  $this->log->notice('LDAP: ' . $a_name . ' account disabled.');
455  return;
456  }
457  }
458 
459  $account = $user_data[strtolower($this->settings->getUserAttribute())];
460  if (is_array($account)) {
461  $user_ext = strtolower(array_shift($account));
462  } else {
463  $user_ext = strtolower($account);
464  }
465 
466  // auth mode depends on ldap server settings
467  $auth_mode = $this->settings->getAuthenticationMappingKey();
468  $user_data['ilInternalAccount'] = ilObjUser::_checkExternalAuthAccount($auth_mode, $user_ext);
469  $this->users[$user_ext] = $user_data;
470  }
471  return true;
472  }
473 
478  private function parseAuthMode()
479  {
480  return $this->settings->getAuthenticationMappingKey();
481  }
482 
492  private function queryByScope($a_scope, $a_base_dn, $a_filter, $a_attributes)
493  {
494  $a_filter = $a_filter ? $a_filter : "(objectclass=*)";
495 
496  switch ($a_scope) {
497  case IL_LDAP_SCOPE_SUB:
498  $res = @ldap_search($this->lh, $a_base_dn, $a_filter, $a_attributes);
499  break;
500 
501  case IL_LDAP_SCOPE_ONE:
502  $res = @ldap_list($this->lh, $a_base_dn, $a_filter, $a_attributes);
503  break;
504 
505  case IL_LDAP_SCOPE_BASE:
506 
507  $res = @ldap_read($this->lh, $a_base_dn, $a_filter, $a_attributes);
508  break;
509 
510  default:
511  $this->log->warning("LDAP: LDAPQuery: Unknown search scope");
512  }
513 
514  $error = ldap_error($this->lh);
515  if (strcmp('Success', $error) !== 0) {
516  $this->getLogger()->warning($error);
517  $this->getLogger()->warning('Base DN:' . $a_base_dn);
518  $this->getLogger()->warning('Filter: ' . $a_filter);
519  }
520 
521  return $res;
522  }
523 
531  private function connect()
532  {
533  $this->lh = @ldap_connect($this->ldap_server_url);
534 
535  // LDAP Connect
536  if (!$this->lh) {
537  throw new ilLDAPQueryException("LDAP: Cannot connect to LDAP Server: " . $this->settings->getUrl());
538  }
539  // LDAP Version
540  if (!ldap_set_option($this->lh, LDAP_OPT_PROTOCOL_VERSION, $this->settings->getVersion())) {
541  throw new ilLDAPQueryException("LDAP: Cannot set version to: " . $this->settings->getVersion());
542  }
543  // Switch on referrals
544  if ($this->settings->isActiveReferrer()) {
545  if (!ldap_set_option($this->lh, LDAP_OPT_REFERRALS, true)) {
546  throw new ilLDAPQueryException("LDAP: Cannot switch on LDAP referrals");
547  }
548  #@ldap_set_rebind_proc($this->lh,'referralRebind');
549  } else {
550  ldap_set_option($this->lh, LDAP_OPT_REFERRALS, false);
551  $this->log->debug('Switching referrals to false.');
552  }
553  // Start TLS
554  if ($this->settings->isActiveTLS()) {
555  if (!ldap_start_tls($this->lh)) {
556  throw new ilLDAPQueryException("LDAP: Cannot start LDAP TLS");
557  }
558  }
559  }
560 
569  public function bind($a_binding_type = IL_LDAP_BIND_DEFAULT, $a_user_dn = '', $a_password = '')
570  {
571  switch ($a_binding_type) {
572  case IL_LDAP_BIND_TEST:
573  ldap_set_option($this->lh, LDAP_OPT_NETWORK_TIMEOUT, ilLDAPServer::DEFAULT_NETWORK_TIMEOUT);
574  // fall through
575  // no break
577  // Now bind anonymously or as user
578  if (
579  IL_LDAP_BIND_USER == $this->settings->getBindingType() &&
580  strlen($this->settings->getBindUser())
581  ) {
582  $user = $this->settings->getBindUser();
583  $pass = $this->settings->getBindPassword();
584 
585  define('IL_LDAP_REBIND_USER', $user);
586  define('IL_LDAP_REBIND_PASS', $pass);
587  $this->log->debug('Bind as ' . $user);
588  } else {
589  $user = $pass = '';
590  $this->log->debug('Bind anonymous');
591  }
592  break;
593 
594  case IL_LDAP_BIND_ADMIN:
595  $user = $this->settings->getRoleBindDN();
596  $pass = $this->settings->getRoleBindPassword();
597 
598  if (!strlen($user) or !strlen($pass)) {
599  $user = $this->settings->getBindUser();
600  $pass = $this->settings->getBindPassword();
601  }
602 
603  define('IL_LDAP_REBIND_USER', $user);
604  define('IL_LDAP_REBIND_PASS', $pass);
605  break;
606 
607  case IL_LDAP_BIND_AUTH:
608  $this->log->debug('Trying to bind as: ' . $a_user_dn);
609  $user = $a_user_dn;
610  $pass = $a_password;
611  break;
612 
613 
614  default:
615  throw new ilLDAPQueryException('LDAP: unknown binding type in: ' . __METHOD__);
616  }
617 
618  if (!@ldap_bind($this->lh, $user, $pass)) {
619  throw new ilLDAPQueryException('LDAP: Cannot bind as ' . $user . ' with message: ' . ldap_err2str(ldap_errno($this->lh)) . ' Trying fallback...', ldap_errno($this->lh));
620  } else {
621  $this->log->debug('Bind successful.');
622  }
623  }
624 
632  private function fetchUserProfileFields()
633  {
634  include_once('Services/LDAP/classes/class.ilLDAPRoleAssignmentRules.php');
635 
636  $this->user_fields = array_merge(
637  array($this->settings->getUserAttribute()),
638  array('dn'),
639  $this->mapping->getFields(),
641  );
642  }
643 
644 
652  private function unbind()
653  {
654  if ($this->lh) {
655  @ldap_unbind($this->lh);
656  }
657  }
658 
659 
667  public function __destruct()
668  {
669  if ($this->lh) {
670  @ldap_unbind($this->lh);
671  }
672  }
673 }
674 
675 function referralRebind($a_ds, $a_url)
676 {
677  global $DIC;
678 
679  $ilLog = $DIC['ilLog'];
680 
681  $ilLog->write('LDAP: Called referralRebind.');
682 
683  ldap_set_option($a_ds, LDAP_OPT_PROTOCOL_VERSION, 3);
684 
685  if (!ldap_bind($a_ds, IL_LDAP_REBIND_USER, IL_LDAP_REBIND_PASS)) {
686  $ilLog->write('LDAP: Rebind failed');
687  }
688 }
const IL_LDAP_BIND_USER
settings()
Definition: settings.php:2
getLogger()
Get logger.
fetchUsers()
Fetch all users.
modAdd($a_dn, $a_attribute)
Add value to an existing attribute.
static _getInstanceByServerId($a_server_id)
Get instance of class.
global $DIC
Definition: saml.php:7
const IL_LDAP_BIND_AUTH
const IL_LDAP_BIND_DEFAULT
const IL_LDAP_SCOPE_SUB
__construct(ilLDAPServer $a_server, $a_url='')
Constructur.
checkGroupMembership($a_ldap_user_name, $ldap_user_data)
check group membership
connect()
Connect to LDAP server.
static getAttributeNames($a_server_id)
get all possible attribute names
const IL_LDAP_BIND_TEST
readAllUsers()
Fetch all users This function splits the query to filters like e.g (uid=a*) (uid=b*)...
fetchGroupMembers($a_name='')
Fetch group member ids.
const DEFAULT_NETWORK_TIMEOUT
parseAuthMode()
Parse authentication mode.
foreach($_POST as $key=> $value) $res
query($a_search_base, $a_filter, $a_scope, $a_attributes)
Perform a query.
fetchUserProfileFields()
fetch required fields of user profile data
referralRebind($a_ds, $a_url)
getServer()
Get server.
fetchUser($a_name)
Get one user by login name.
const IL_LDAP_SCOPE_ONE
readUserData($a_name, $a_check_dn=false, $a_try_group_user_filter=false)
Read user data.
modDelete($a_dn, $a_attribute)
Delete value from an existing attribute.
$user
Definition: migrateto20.php:57
const IL_LDAP_BIND_ADMIN
$users
Definition: authpage.php:44
static _checkExternalAuthAccount($a_auth, $a_account, $tryFallback=true)
check whether external account and authentication method matches with a user
__destruct()
Destructor unbind from ldap server.
bind($a_binding_type=IL_LDAP_BIND_DEFAULT, $a_user_dn='', $a_password='')
Bind to LDAP server.
const IL_LDAP_SCOPE_BASE
queryByScope($a_scope, $a_base_dn, $a_filter, $a_attributes)
Query by scope IL_SCOPE_SUB => ldap_search IL_SCOPE_ONE => ldap_list.
$GLOBALS['JPEG_Segment_Names']
Global Variable: XMP_tag_captions.
$data
Definition: bench.php:6