Projet

Général

Profil

Paste
Télécharger (15,8 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / ldap / ldap_test / LdapServerTest.class.php @ 7547bb19

1
<?php
2

    
3
/**
4
 * @file
5
 * Simpletest ldapServer class for testing without an actual ldap server
6
 *
7
 */
8

    
9
/**
10
 * LDAP Server Class
11
 *
12
 *  This class is used to create, work with, and eventually destroy ldap_server
13
 * objects.
14
 *
15
 * @todo make bindpw protected
16
 */
17

    
18
ldap_servers_module_load_include('php', 'ldap_servers', 'LdapServer.class');
19

    
20
class LdapServerTest extends LdapServer {
21

    
22
  public $entries;
23
  public $methodResponses;
24
  public $searchResults;
25
  public $binddn = FALSE; // Default to an anonymous bind.
26
  public $bindpw = FALSE; // Default to an anonymous bind.
27

    
28
  /**
29
   * Constructor Method
30
   *
31
   * can take array of form property_name => property_value
32
   * or $sid, where sid is used to derive the include file.
33
   */
34
  function __construct($sid) {
35
    if (!is_scalar($sid)) {
36
      $test_data = $sid;
37
      $sid = $test_data['sid'];
38
    }
39
    else {
40
      $test_data = variable_get('ldap_test_server__' . $sid, array());
41
    }
42

    
43
    $bindpw = (isset($test_data['bindpw'])) ? $test_data['bindpw'] : 'goodpwd';
44
    $this->sid = $sid;
45
    $this->refreshFakeData();
46
    $this->initDerivedProperties($bindpw);
47
  }
48

    
49
  public function refreshFakeData() {
50
   // debug("refreshFakeData sid=". $this->sid);
51
    $test_data = variable_get('ldap_test_server__' . $this->sid, array());
52
    $this->methodResponses = (is_array($test_data) && isset($test_data['methodResponses'])) ? $test_data['methodResponses'] : array();
53
    $this->entries = (is_array($test_data) && isset($test_data['ldap'])) ? $test_data['ldap'] : array();
54
  //  debug('this->entries');debug($this->entries);
55
    $this->searchResults = (isset($test_data['search_results'])) ? $test_data['search_results'] : array();
56
    $this->detailedWatchdogLog = variable_get('ldap_help_watchdog_detail', 0);
57
    foreach ($test_data['properties'] as $property_name => $property_value ) {
58
      $this->{$property_name} = $property_value;
59
    }
60
  //  $this->basedn = unserialize($this->basedn);
61
    if (isset($test_data['bindpw']) && $test_data['bindpw'] != '') {
62
      $this->bindpw = ldap_servers_decrypt($this->bindpw);
63
    }
64
  }
65

    
66
  /**
67
   * Destructor Method
68
   */
69
  function __destruct() {
70
     // if alterations to server configuration must be maintained throughout simpletest, variable_set('ldap_authorization_test_server__'. $sid, array());
71
  }
72

    
73
  /**
74
   * Connect Method
75
   */
76
  function connect() {
77
    return $this->methodResponses['connect'];
78
  }
79

    
80

    
81
  function bind($userdn = NULL, $pass = NULL, $anon_bind = FALSE) {
82
    $userdn = ($userdn != NULL) ? $userdn : $this->binddn;
83
    $pass = ($pass != NULL) ? $pass : $this->bindpw;
84

    
85
    if (! isset($this->entries[$userdn])) {
86
      $ldap_errno = LDAP_NO_SUCH_OBJECT;  // 0x20 or 32
87
      if (function_exists('ldap_err2str')) {
88
        $ldap_error = ldap_err2str($ldap_errno);
89
      }
90
      else {
91
        $ldap_error = "Failed to find $userdn in LdapServerTest.class.php";
92
      }
93
    }
94
    elseif (isset($this->entries[$userdn]['password'][0]) && $this->entries[$userdn]['password'][0] == $pass && $pass) {
95
      return LDAP_SUCCESS;
96
    }
97
    else {
98
      if (!$pass) {
99
        debug("Simpletest failure for $userdn.  No password submitted");
100
      }
101
      if (! isset($this->entries[$userdn]['password'][0])) {
102
        debug("Simpletest failure for $userdn.  No password in entry to test for bind"); debug($this->entries[$userdn]);
103
      }
104
      $ldap_errno = LDAP_INVALID_CREDENTIALS;
105
      if (function_exists('ldap_err2str')) {
106
        $ldap_error = ldap_err2str($ldap_errno);
107
      }
108
      else {
109
        $ldap_error = "Credentials for $userdn failed in LdapServerTest.class.php";
110
      }
111
    }
112

    
113
    $watchdog_tokens = array('%user' => $userdn, '%errno' => $ldap_errno, '%error' => $ldap_error);
114
    watchdog('ldap', "LDAP bind failure for user %user. Error %errno: %error", $watchdog_tokens);
115
    return $ldap_errno;
116

    
117
  }
118

    
119
  /**
120
   * Disconnect (unbind) from an active LDAP server.
121
   */
122
  function disconnect() {
123

    
124
  }
125

    
126
  /**
127
   * Perform an LDAP search.
128
   *
129
   * @param string $filter
130
   *   The search filter. such as sAMAccountName=jbarclay
131
   * @param string $basedn
132
   *   The search base. If NULL, we use $this->basedn
133
   * @param array $attributes
134
   *   List of desired attributes. If omitted, we only return "dn".
135
   *
136
   * @return
137
   *   An array of matching entries->attributes, or FALSE if the search is
138
   *   empty.
139
   */
140
  function search($base_dn = NULL, $filter, $attributes = array(), $attrsonly = 0, $sizelimit = 0, $timelimit = 0, $deref = LDAP_DEREF_NEVER, $scope = LDAP_SCOPE_SUBTREE) {
141

    
142
   // debug("ldap test server search base_dn=$base_dn, filter=$filter");
143

    
144
    $lcase_attribute = array();
145
    foreach ($attributes as $i => $attribute_name) {
146
      $lcase_attribute[] = drupal_strtolower($attribute_name);
147
    }
148
    $attributes = $lcase_attribute;
149

    
150
    $filter = trim(str_replace(array("\n", "  "), array('', ''), $filter)); // for test matching simplicity remove line breaks and tab spacing
151

    
152
    if ($base_dn == NULL) {
153
      if (count($this->basedn) == 1) {
154
        $base_dn = $this->basedn[0];
155
      }
156
      else {
157
        // debug("fail basedn: ldap test server search base_dn=$base_dn, filter=$filter");
158
        return FALSE;
159
      }
160
    }
161

    
162
    /**
163
     * Search CASE 1: for some mock ldap servers, a set of fixed ldap filters
164
     * are prepolulated in test data
165
     */
166
    if (isset($this->searchResults[$filter][$base_dn])) {
167
   //   debug("case1 filter= $filter   base_dn=$base_dn ");
168
      $results = $this->searchResults[$filter][$base_dn];
169
      foreach ($results as $i => $entry) {
170
        if (is_array($entry) && isset($entry['FULLENTRY'])) {
171
          unset($results[$i]['FULLENTRY']);
172
          $dn = $results[$i]['dn'];
173
          $results[$i] = $this->entries[$dn];
174
          $results[$i]['dn'] = $dn;
175
        }
176
      }
177
   //   debug($results);
178
      return $results; 
179
    }
180

    
181
    /**
182
     * Search CASE 2: attempt to programmatically evaluate ldap filter
183
     * by looping through fake ldap entries
184
     */
185
    $base_dn = drupal_strtolower($base_dn);
186
    $filter = trim($filter, "()");
187
    $subqueries = array();
188
    $operand = FALSE;
189

    
190
    if (strpos($filter, '&') === 0) {
191
   //   debug('2.A.');
192
     /**
193
     * case 2.A.: filter of form (&(<attribute>=<value>)(<attribute>=<value>)(<attribute>=<value>))
194
     *  such as (&(samaccountname=hpotter)(samaccountname=hpotter)(samaccountname=hpotter))
195
     */
196
      $operand = '&';
197
      $filter = substr($filter, 1);
198
      $filter = trim($filter, "()");
199
      $parts = explode(')(', $filter);
200
      foreach ($parts as $i => $pair) {
201
        $subqueries[] = explode('=', $pair);
202
      }
203
    }
204
    elseif (strpos($filter, '|') === 0) {
205
      //debug('2.B.');
206
     /**
207
     * case 2.B: filter of form (|(<attribute>=<value>)(<attribute>=<value>)(<attribute>=<value>))
208
     *  such as (|(samaccountname=hpotter)(samaccountname=hpotter)(samaccountname=hpotter))
209
     */
210
      $operand = '|';
211
      $filter = substr($filter, 1);
212
      $filter = trim($filter, "()");
213
      $parts = explode(')(', $filter);
214
      $parts = explode(')(', $filter);
215
      foreach ($parts as $i => $pair) {
216
        $subqueries[] = explode('=', $pair);
217
      }
218
         // debug("operand=$operand, filter=$filter subqueries"); debug($subqueries);
219
   // debug($this->entries['cn=hpotter,ou=people,dc=hogwarts,dc=edu']);
220
   // debug($this->entries['cn=clone0,ou=people,dc=hogwarts,dc=edu']);
221
    }
222
    elseif (count(explode('=', $filter)) == 2) {
223
     // debug('2.C.');
224
     /**
225
     * case 2.C.: filter of form (<attribute>=<value>)
226
     *  such as (samaccountname=hpotter)
227
     */
228
      $operand = '|';
229
      $subqueries[] = explode('=', $filter);
230
    }
231
    else {
232
    //  debug('no case');
233
      return FALSE;
234
    }
235

    
236

    
237

    
238

    
239
    // need to perform feaux ldap search here with data in
240
    $results = array();
241

    
242
    if ($operand == '|') {
243
      foreach ($subqueries as $i => $subquery) {
244
        $filter_attribute = drupal_strtolower($subquery[0]);
245
        $filter_value = $subquery[1];
246
      //  debug("filter_attribute=$filter_attribute, filter_value=$filter_value");
247
        foreach ($this->entries as $dn => $entry) {
248
          $dn_lcase = drupal_strtolower($dn);
249

    
250
          // if not in basedn, skip
251
          // eg. basedn ou=campus accounts,dc=ad,dc=myuniversity,dc=edu
252
          // should be leftmost string in:
253
          // cn=jdoe,ou=campus accounts,dc=ad,dc=myuniversity,dc=edu
254
          //$pos = strpos($dn_lcase, $base_dn);
255
          $substring = strrev(substr(strrev($dn_lcase), 0, strlen($base_dn)));
256
          $cascmp =  strcasecmp($base_dn, $substring);
257
          //debug("dn_lcase=$dn_lcase, base_dn=$base_dn,pos=$pos,substring=$substring,cascmp=$cascmp");
258
          if ($cascmp !== 0) {
259

    
260
            continue; // not in basedn
261
          }
262
          // if doesn't filter attribute has no data, continue
263
          $attr_value_to_compare = FALSE;
264
          foreach ($entry as $attr_name => $attr_value) {
265
            if (drupal_strtolower($attr_name) == $filter_attribute) {
266
              $attr_value_to_compare = $attr_value;
267
              break;
268
            }
269
          }
270
         // debug("filter value=$filter_value, attr_value_to_compare="); debug($attr_value_to_compare);
271
          if (!$attr_value_to_compare || drupal_strtolower($attr_value_to_compare[0]) != $filter_value) {
272
            continue;
273
          }
274

    
275
          // match!
276
         // debug("match"); debug($attr_value); debug($attributes);
277
          $entry['dn'] = $dn;
278
          if ($attributes) {
279
            $selected_data = array();
280
            foreach ($attributes as $i => $attr_name) {
281
              $selected_data[$attr_name] = (isset($entry[$attr_name])) ? $entry[$attr_name] : NULL;
282
            }
283
            $results[] = $selected_data;
284
          }
285
          else {
286
            $results[] = $entry;
287
          }
288
        }
289
      }
290
    }
291
    elseif ($operand == '&') { // reverse the loops
292
      foreach ($this->entries as $dn => $entry) {
293
        $dn_lcase = drupal_strtolower($dn);
294
        $match = TRUE; // until 1 subquery fails
295
        foreach ($subqueries as $i => $subquery) {
296
          $filter_attribute = drupal_strtolower($subquery[0]);
297
          $filter_value = $subquery[1];
298

    
299
          $substring = strrev(substr(strrev($dn_lcase), 0, strlen($base_dn)));
300
          $cascmp =  strcasecmp($base_dn, $substring);
301
          //debug("dn_lcase=$dn_lcase, base_dn=$base_dn,pos=$pos,substring=$substring,cascmp=$cascmp");
302
          if ($cascmp !== 0) {
303
            $match = FALSE;
304
            break; // not in basedn
305
          }
306
          // if doesn't filter attribute has no data, continue
307
          $attr_value_to_compare = FALSE;
308
          foreach ($entry as $attr_name => $attr_value) {
309
            if (drupal_strtolower($attr_name) == $filter_attribute) {
310
              $attr_value_to_compare = $attr_value;
311
              break;
312
            }
313
          }
314
         // debug("filter value=$filter_value, attr_value_to_compare="); debug($attr_value_to_compare);
315
          if (!$attr_value_to_compare || drupal_strtolower($attr_value_to_compare[0]) != $filter_value) {
316
            $match = FALSE;
317
            break; // not in basedn
318
          }
319

    
320
        }
321
        if ($match === TRUE) {
322
          $entry['dn'] = $dn;
323
          if ($attributes) {
324
            $selected_data = array();
325
            foreach ($attributes as $i => $attr_name) {
326
              $selected_data[$attr_name] = (isset($entry[$attr_name])) ? $entry[$attr_name] : NULL;
327
            }
328
            $results[] = $selected_data;
329
          }
330
          else {
331
            $results[] = $entry;
332
          }
333
        }
334
      }
335
    }
336

    
337
    $results['count'] = count($results);
338
  //  debug("ldap test server search results"); debug($results);
339
    return $results;
340
  }
341

    
342
/**
343
 * does dn exist for this server?
344
 *
345
 * @param string $dn
346
 * @param enum $return = 'boolean' or 'ldap_entry'
347
 *
348
 * @param return FALSE or ldap entry array
349
 */
350
  function dnExists($find_dn, $return = 'boolean', $attributes = array('objectclass')) {
351
    $this->refreshFakeData();
352
    $test_data = variable_get('ldap_test_server__' . $this->sid, array());
353
    foreach ($this->entries as $entry_dn => $entry) {
354
      $match = (strcasecmp($entry_dn, $find_dn) == 0);
355

    
356
      if ($match) {
357
      //  debug("testserver:dnExists,match=$match, entry_dn=$entry_dn, find_dn=$find_dn"); debug($entry);
358
        return ($return == 'boolean') ? TRUE : $entry;
359
      }
360
    }
361
   // debug("testserver:dnExists, no match");
362
    return FALSE; // not match found in loop
363

    
364
  }
365

    
366
  public function countEntries($ldap_result) {
367
    return ldap_count_entries($this->connection, $ldap_result);
368
  }
369

    
370

    
371
  public static function getLdapServerObjects($sid = NULL, $type = NULL, $flatten = FALSE) {
372
    $servers = array();
373
    if ($sid) {
374
      $servers[$sid] = new LdapServerTest($sid);
375
    }
376
    else {
377
      $server_ids = variable_get('ldap_test_servers', array());
378
      foreach ($server_ids as $sid => $_sid) {
379
        $servers[$sid] = new LdapServerTest($sid);
380
      }
381
    }
382

    
383
    if ($flatten && $sid) {
384
      return $servers[$sid];
385
    }
386
    else {
387
      return $servers;
388
    }
389
  }
390

    
391

    
392
  /**
393
   * create ldap entry.
394
   *
395
   * @param array $ldap_entry should follow the structure of ldap_add functions
396
   *   entry array: http://us.php.net/manual/en/function.ldap-add.php
397
        $attributes["attribute1"] = "value";
398
        $attributes["attribute2"][0] = "value1";
399
        $attributes["attribute2"][1] = "value2";
400
   * @return boolean result
401
   */
402

    
403
  public function createLdapEntry($ldap_entry, $dn = NULL) {
404
    $result = FALSE;
405
    $test_data = variable_get('ldap_test_server__' . $this->sid, array());
406

    
407
    if (isset($ldap_entry['dn'])) {
408
      $dn = $ldap_entry['dn'];
409
      unset($ldap_entry['dn']);
410
    }
411

    
412
    if ($dn && !isset($test_data['entries'][$dn])) {
413
      $test_data['entries'][$dn] = $ldap_entry;
414
      $test_data['ldap'][$dn] = $ldap_entry;
415
      variable_set('ldap_test_server__' . $this->sid, $test_data);
416
      $this->refreshFakeData();
417
      $result = TRUE;
418
    }
419
    return $result;
420
  }
421

    
422
  function modifyLdapEntry($dn, $attributes = NULL, $old_attributes = FALSE) {
423
    if (!$attributes) {
424
      $attributes = array();
425
    }
426
    $test_data = variable_get('ldap_test_server__' . $this->sid, array());
427
    if (!isset($test_data['entries'][$dn])) {
428
      return FALSE;
429
    }
430
    $ldap_entry = $test_data['entries'][$dn];
431

    
432
    foreach ($attributes as $key => $cur_val) {
433
      if ($cur_val == '') {
434
        unset($ldap_entry[$key]);
435
      }
436
      elseif (is_array($cur_val)) {
437
        foreach ($cur_val as $mv_key => $mv_cur_val) {
438
          if ($mv_cur_val == '') {
439
            unset($ldap_entry[$key][$mv_key]);
440
          }
441
          else {
442
            if (is_array($mv_cur_val)) {
443
              $ldap_entry[$key][$mv_key] = $mv_cur_val;
444
            }
445
            else {
446
              $ldap_entry[$key][$mv_key][] = $mv_cur_val;
447
            }
448
          }
449
        }
450
        unset($ldap_entry[$key]['count']);
451
        $ldap_entry[$key]['count'] = count($ldap_entry[$key]);
452
      }
453
      else {
454
        $ldap_entry[$key][0] = $cur_val;
455
        $ldap_entry[$key]['count'] = count($ldap_entry[$key]);
456
      }
457
    }
458

    
459
    $test_data['entries'][$dn] = $ldap_entry;
460
    $test_data['ldap'][$dn] = $ldap_entry;
461
  //  debug("modifyLdapEntry:server test data before save $dn"); debug($test_data['entries'][$dn]);
462
    variable_set('ldap_test_server__' . $this->sid, $test_data);
463
    $this->refreshFakeData();
464
    return TRUE;
465

    
466
  }
467

    
468
    /**
469
   * Perform an LDAP delete.
470
   *
471
   * @param string $dn
472
   *
473
   * @return boolean result per ldap_delete
474
   */
475

    
476
  public function delete($dn) {
477

    
478
    $test_data = variable_get('ldap_test_server__' . $this->sid, array());
479
   // debug("test ldap server, delete=$dn, test data="); debug(array_keys($test_data['users']));
480
    $deleted = FALSE;
481
    foreach (array('entries', 'users', 'groups', 'ldap') as $test_data_sub_array) {
482
      if (isset($test_data[$test_data_sub_array][$dn])) {
483
        unset($test_data[$test_data_sub_array][$dn]);
484
        $deleted = TRUE;
485
      }
486
    }
487
    if ($deleted) {
488
      variable_set('ldap_test_server__' . $this->sid, $test_data);
489
      $this->refreshFakeData();
490
      return TRUE;
491
    }
492
    else {
493
      return FALSE;
494
    }
495

    
496
  }
497

    
498
}