Projet

Général

Profil

Paste
Télécharger (50,9 ko) Statistiques
| Branche: | Révision:

root / htmltest / sites / all / modules / ldap / ldap_user / LdapUserConf.class.php @ dd54aff9

1
<?php
2

    
3
/**
4
 * @file
5
 * This class represents a ldap_user module's configuration
6
 * It is extended by LdapUserConfAdmin for configuration and other admin functions
7
 */
8

    
9
require_once('ldap_user.module');
10

    
11
class LdapUserConf {
12

    
13
  /**
14
   * server providing Drupal account provisioning
15
   *
16
   * @var string
17
   *
18
   * @see LdapServer::sid
19
   */
20
  public $drupalAcctProvisionServer = LDAP_USER_NO_SERVER_SID;
21

    
22
  /**
23
   * server providing LDAP entry provisioning
24
   *
25
   * @var string
26
   *
27
   * @see LdapServer::sid
28
   */
29
  public $ldapEntryProvisionServer = LDAP_USER_NO_SERVER_SID;
30

    
31
  /**
32
   * Associative array mapping synch directions to ldap server instances.
33
   *
34
   * @var array
35
   */
36
  public $provisionSidFromDirection = array(
37
    LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER => LDAP_USER_NO_SERVER_SID,
38
    LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY => LDAP_USER_NO_SERVER_SID,
39
  );
40

    
41
  /**
42
   * Array of events that trigger provisioning of Drupal Accounts
43
   * Valid constants are:
44
   *   LDAP_USER_DRUPAL_USER_PROV_ON_AUTHENTICATE
45
   *   LDAP_USER_DRUPAL_USER_PROV_ON_USER_UPDATE_CREATE
46
   *   LDAP_USER_DRUPAL_USER_PROV_ON_ALLOW_MANUAL_CREATE
47
   *
48
   * @var array
49
   */
50
  public $drupalAcctProvisionTriggers = array(LDAP_USER_DRUPAL_USER_PROV_ON_AUTHENTICATE, LDAP_USER_DRUPAL_USER_PROV_ON_USER_UPDATE_CREATE, LDAP_USER_DRUPAL_USER_PROV_ON_ALLOW_MANUAL_CREATE);
51

    
52
  /**
53
   * Array of events that trigger provisioning of LDAP Entries
54
   * Valid constants are:
55
   *   LDAP_USER_LDAP_ENTRY_PROV_ON_USER_UPDATE_CREATE
56
   *   LDAP_USER_LDAP_ENTRY_PROV_ON_AUTHENTICATE
57
   *   LDAP_USER_LDAP_ENTRY_DELETE_ON_USER_DELETE
58
   *
59
   * @var array
60
   */
61
  public $ldapEntryProvisionTriggers = array();
62

    
63
  /**
64
   * server providing LDAP entry provisioning
65
   *
66
   * @var string
67
   *
68
   * @see LdapServer::sid
69
   */
70
  public $userConflictResolve = LDAP_USER_CONFLICT_RESOLVE_DEFAULT;
71

    
72
  /**
73
   * drupal account creation model
74
   *
75
   * @var int
76
   *   LDAP_USER_ACCT_CREATION_LDAP_BEHAVIOR   /admin/config/people/accounts/settings do not affect "LDAP Associated" Drupal accounts.
77
   *   LDAP_USER_ACCT_CREATION_USER_SETTINGS_FOR_LDAP  use Account creation settings at /admin/config/people/accounts/settings
78
   */
79
  public $acctCreation = LDAP_USER_ACCT_CREATION_LDAP_BEHAVIOR_DEFAULT;
80

    
81
  /**
82
   * has current object been saved to the database?
83
   *
84
   * @var boolean
85
   *
86
   */
87
  public $inDatabase = FALSE;
88

    
89
  /**
90
   * what to do when an ldap provisioned username conflicts with existing drupal user?
91
   *
92
   * @var int
93
   *   LDAP_USER_CONFLICT_LOG - log the conflict
94
   *   LDAP_USER_CONFLICT_RESOLVE - LDAP associate the existing drupal user
95
   *
96
   */
97
  public $manualAccountConflict = LDAP_USER_MANUAL_ACCT_CONFLICT_REJECT;
98

    
99
  public $setsLdapPassword = TRUE; // @todo default to FALSE and check for mapping to set to true
100

    
101
  public $loginConflictResolve = FALSE;
102

    
103
  public $disableAdminPasswordField = FALSE;
104
  /**
105
   * array of field synch mappings provided by all modules (via hook_ldap_user_attrs_list_alter())
106
   * array of the form: array(
107
   * LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER | LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY => array(
108
   *   <server_id> => array(
109
   *     'sid' => <server_id> (redundant)
110
   *     'ldap_attr' => e.g. [sn]
111
   *     'user_attr'  => e.g. [field.field_user_lname] (when this value is set to 'user_tokens', 'user_tokens' value is used.)
112
   *     'user_tokens' => e.g. [field.field_user_lname], [field.field_user_fname]
113
   *     'convert' => 1|0 boolean indicating need to covert from binary
114
   *     'direction' => LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER | LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY (redundant)
115
   *     'config_module' => 'ldap_user'
116
   *     'prov_module' => 'ldap_user'
117
   *     'enabled' => 1|0 boolean
118
   *      prov_events' => array( of LDAP_USER_EVENT_* constants indicating during which synch actions field should be synched)
119
   *         - four permutations available
120
   *            to ldap:   LDAP_USER_EVENT_CREATE_LDAP_ENTRY,  LDAP_USER_EVENT_SYNCH_TO_LDAP_ENTRY,
121
   *            to drupal: LDAP_USER_EVENT_CREATE_DRUPAL_USER, LDAP_USER_EVENT_SYNCH_TO_DRUPAL_USER
122
   *    )
123
   *  )
124
   */
125
  public $synchMapping = NULL; // array of field synching directions for each operation.  should include ldapUserSynchMappings
126
  // keyed on direction => property, ldap, or field token such as '[field.field_lname] with brackets in them.
127

    
128
  /**
129
  * synch mappings configured in ldap user module (not in other modules)
130
  *   array of the form: array(
131
    LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER | LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY => array(
132
      'sid' => <server_id> (redundant)
133
      'ldap_attr' => e.g. [sn]
134
      'user_attr'  => e.g. [field.field_user_lname] (when this value is set to 'user_tokens', 'user_tokens' value is used.)
135
      'user_tokens' => e.g. [field.field_user_lname], [field.field_user_fname]
136
      'convert' => 1|0 boolean indicating need to covert from binary
137
      'direction' => LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER | LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY (redundant)
138
      'config_module' => 'ldap_user'
139
      'prov_module' => 'ldap_user'
140
      'enabled' => 1|0 boolean
141
       prov_events' => array( of LDAP_USER_EVENT_* constants indicating during which synch actions field should be synched)
142
          - four permutations available
143
             to ldap:   LDAP_USER_EVENT_CREATE_LDAP_ENTRY,  LDAP_USER_EVENT_SYNCH_TO_LDAP_ENTRY,
144
             to drupal: LDAP_USER_EVENT_CREATE_DRUPAL_USER, LDAP_USER_EVENT_SYNCH_TO_DRUPAL_USER
145
      )
146
    )
147
  )
148
  */
149
  public $ldapUserSynchMappings = NULL;  //
150
  // keyed on property, ldap, or field token such as '[field.field_lname] with brackets in them.
151
  public $detailedWatchdog = FALSE;
152
  public $provisionsDrupalAccountsFromLdap = FALSE;
153
  public $provisionsLdapEntriesFromDrupalUsers = FALSE;
154

    
155
  // what should be done with ldap provisioned accounts that no longer have associated drupal accounts.
156
  public $orphanedDrupalAcctBehavior = 'ldap_user_orphan_email';
157
   /** options are partially derived from user module account cancel options:
158
    *
159
    'ldap_user_orphan_do_not_check' => Do not check for orphaned Drupal accounts.)
160
    'ldap_user_orphan_email' => Perform no action, but email list of orphaned accounts. (All the other options will send email summaries also.)
161
    'user_cancel_block' => Disable the account and keep its content.
162
    'user_cancel_block_unpublish' => Disable the account and unpublish its content.
163
    'user_cancel_reassign' => Delete the account and make its content belong to the Anonymous user.
164
    'user_cancel_delete' => Delete the account and its content.
165
    */
166

    
167
  public $orphanedCheckQty = 100;
168

    
169
// public $wsKey = NULL;
170
//  public $wsEnabled = 0;
171
//  public $wsUserIps = array();
172

    
173
  public $provisionsLdapEvents = array();
174
  public $provisionsDrupalEvents = array();
175

    
176
  public $saveable = array(
177
    'drupalAcctProvisionServer',
178
    'ldapEntryProvisionServer',
179
    'drupalAcctProvisionTriggers',
180
    'ldapEntryProvisionTriggers',
181
    'orphanedDrupalAcctBehavior',
182
    'orphanedCheckQty',
183
    'userConflictResolve',
184
    'manualAccountConflict',
185
    'acctCreation',
186
    'ldapUserSynchMappings',
187
    'disableAdminPasswordField',
188
  );
189
// 'wsKey','wsEnabled','wsUserIps',
190
  function __construct() {
191
    $this->load();
192

    
193
    $this->provisionSidFromDirection[LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER] = $this->drupalAcctProvisionServer;
194
    $this->provisionSidFromDirection[LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY] = $this->ldapEntryProvisionServer;
195

    
196
    $this->provisionsLdapEvents = array(
197
      LDAP_USER_EVENT_CREATE_LDAP_ENTRY => t('On LDAP Entry Creation'),
198
      LDAP_USER_EVENT_SYNCH_TO_LDAP_ENTRY => t('On Synch to LDAP Entry'),
199
      );
200

    
201
    $this->provisionsDrupalEvents = array(
202
      LDAP_USER_EVENT_CREATE_DRUPAL_USER => t('On Drupal User Creation'),
203
      LDAP_USER_EVENT_SYNCH_TO_DRUPAL_USER => t('On Synch to Drupal User'),
204
      );
205

    
206
    $this->provisionsDrupalAccountsFromLdap = (
207
      $this->drupalAcctProvisionServer &&
208
      $this->drupalAcctProvisionServer &&
209
      (count(array_filter(array_values($this->drupalAcctProvisionTriggers))) > 0)
210
    );
211

    
212
    $this->provisionsLdapEntriesFromDrupalUsers = (
213
      $this->ldapEntryProvisionServer
214
      && $this->ldapEntryProvisionServer
215
      && (count(array_filter(array_values($this->ldapEntryProvisionTriggers))) > 0)
216
      );
217

    
218
    $this->setSynchMapping(TRUE);
219
    $this->detailedWatchdog = variable_get('ldap_help_watchdog_detail', 0);
220
  }
221

    
222
  function load() {
223

    
224
    if ($saved = variable_get("ldap_user_conf", FALSE)) {
225
      $this->inDatabase = TRUE;
226
      foreach ($this->saveable as $property) {
227
        if (isset($saved[$property])) {
228
          $this->{$property} = $saved[$property];
229
        }
230
      }
231
    }
232
    else {
233
      $this->inDatabase = FALSE;
234
    }
235
    // determine account creation configuration
236
    $user_register = variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
237
    if ($this->acctCreation == LDAP_USER_ACCT_CREATION_LDAP_BEHAVIOR_DEFAULT || $user_register == USER_REGISTER_VISITORS) {
238
      $this->createLDAPAccounts = TRUE;
239
      $this->createLDAPAccountsAdminApproval = FALSE;
240
    }
241
    elseif ($user_register == USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) {
242
      $this->createLDAPAccounts = FALSE;
243
      $this->createLDAPAccountsAdminApproval = TRUE;
244
    }
245
    else {
246
      $this->createLDAPAccounts = FALSE;
247
      $this->createLDAPAccountsAdminApproval = FALSE;
248
    }
249
  }
250

    
251
  /**
252
   * Destructor Method
253
   */
254
  function __destruct() { }
255

    
256

    
257
  /**
258
   * Util to fetch mappings for a given direction
259
   *
260
   * @param string $sid
261
   *   The server id
262
   * @param string $direction LDAP_USER_PROV_DIRECTION_* constant
263
   * @param array $prov_events
264
   *
265
   * @return array/bool
266
   *   Array of mappings (may be empty array)
267
  */
268
  public function getSynchMappings($direction = LDAP_USER_PROV_DIRECTION_ALL, $prov_events = NULL) {
269
    if (!$prov_events) {
270
      $prov_events = ldap_user_all_events();
271
    }
272

    
273
    $mappings = array();
274
    if ($direction == LDAP_USER_PROV_DIRECTION_ALL) {
275
      $directions = array(LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER, LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY);
276
    }
277
    else {
278
      $directions = array($direction);
279
    }
280
    foreach ($directions as $direction) {
281
      if (!empty($this->ldapUserSynchMappings[$direction])) {
282
        foreach ($this->ldapUserSynchMappings[$direction] as $attribute => $mapping) {
283
          if (!empty($mapping['prov_events'])) {
284
            $result = count(array_intersect($prov_events, $mapping['prov_events']));
285
            if ($result) {
286
              $mappings[$attribute] = $mapping;
287
            }
288
          }
289
        }
290
      }
291
    }
292
    return $mappings;
293
  }
294

    
295
  public function isDrupalAcctProvisionServer($sid) {
296
    if (!$sid || !$this->drupalAcctProvisionServer) {
297
      return FALSE;
298
    }
299
    elseif ($this->ldapEntryProvisionServer == $sid) {
300
      return TRUE;
301
    }
302
    else {
303
      return FALSE;
304
    }
305
  }
306

    
307
  public function isLdapEntryProvisionServer($sid) {
308
    if (!$sid || !$this->ldapEntryProvisionServer) {
309
      return FALSE;
310
    }
311
    elseif ($this->ldapEntryProvisionServer == $sid) {
312
      return TRUE;
313
    }
314
    else {
315
      return FALSE;
316
    }
317
  }
318

    
319
  /**
320
   * Util to fetch attributes required for this user conf, not other modules.
321
   *
322
   * @param enum $direction LDAP_USER_PROV_DIRECTION_* constants
323
   * @param string $ldap_context
324
   *
325
  */
326
  public function getLdapUserRequiredAttributes($direction = LDAP_USER_PROV_DIRECTION_ALL, $ldap_context = NULL) {
327

    
328
    $attributes_map = array();
329
    $required_attributes = array();
330
    if ($this->drupalAcctProvisionServer) {
331
      $prov_events = $this->ldapContextToProvEvents($ldap_context);
332
      $attributes_map = $this->getSynchMappings($direction, $prov_events);
333
      $required_attributes = array();
334
      foreach ($attributes_map as $detail) {
335
        if (count(array_intersect($prov_events, $detail['prov_events']))) {
336
          // Add the attribute to our array.
337
          if ($detail['ldap_attr']) {
338
            ldap_servers_token_extract_attributes($required_attributes,  $detail['ldap_attr']);
339
          }
340
        }
341
      }
342
    }
343
    return $required_attributes;
344
  }
345

    
346
/**
347
 * converts the more general ldap_context string to its associated ldap user event
348
 */
349

    
350
  public function ldapContextToProvEvents($ldap_context = NULL) {
351

    
352
    switch ($ldap_context) {
353

    
354
      case 'ldap_user_prov_to_drupal':
355
        $result = array(LDAP_USER_EVENT_SYNCH_TO_DRUPAL_USER, LDAP_USER_EVENT_CREATE_DRUPAL_USER, LDAP_USER_EVENT_LDAP_ASSOCIATE_DRUPAL_ACCT);
356
        break;
357

    
358
      case 'ldap_user_prov_to_ldap':
359
        $result = array(LDAP_USER_EVENT_SYNCH_TO_LDAP_ENTRY, LDAP_USER_EVENT_CREATE_LDAP_ENTRY);
360
        break;
361

    
362
      default:
363
        $result = ldap_user_all_events();
364

    
365
    }
366

    
367
    return $result;
368

    
369
  }
370

    
371

    
372
/**
373
 * converts the more general ldap_context string to its associated ldap user prov direction
374
 */
375

    
376
  public function ldapContextToProvDirection($ldap_context = NULL) {
377

    
378
    switch ($ldap_context) {
379

    
380
      case 'ldap_user_prov_to_drupal':
381
        $result = LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER;
382
        break;
383

    
384
      case 'ldap_user_prov_to_ldap':
385
      case 'ldap_user_delete_drupal_user':
386
        $result = LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY;
387
        break;
388

    
389
      // provisioning is can hapen in both directions in most contexts
390
      case 'ldap_user_insert_drupal_user':
391
      case 'ldap_user_update_drupal_user':
392
      case 'ldap_authentication_authenticate':
393
      case 'ldap_user_insert_drupal_user':
394
      case 'ldap_user_disable_drupal_user':
395
        $result = LDAP_USER_PROV_DIRECTION_ALL;
396
        break;
397

    
398
      default:
399
        $result = LDAP_USER_PROV_DIRECTION_ALL;
400

    
401
    }
402

    
403
    return $result;
404
  }
405

    
406
  /**
407
    derive mapping array from ldap user configuration and other configurations.
408
    if this becomes a resource hungry function should be moved to ldap_user functions
409
    and stored with static variable. should be cached also.
410

411
    this should be cached and modules implementing ldap_user_synch_mapping_alter
412
    should know when to invalidate cache.
413

414
   */
415

    
416
  function setSynchMapping($reset = TRUE) {  // @todo change default to false after development
417
    $synch_mapping_cache = cache_get('ldap_user_synch_mapping');
418
    if (!$reset && $synch_mapping_cache) {
419
      $this->synchMapping = $synch_mapping_cache->data;
420
    }
421
    else {
422
      $available_user_attrs = array();
423
      foreach (array(LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER, LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY) as $direction) {
424
        $sid = $this->provisionSidFromDirection[$direction];
425
        $available_user_attrs[$direction] = array();
426
        $ldap_server = ($sid) ? ldap_servers_get_servers($sid, NULL, TRUE) : FALSE;
427

    
428
        $params = array(
429
          'ldap_server' => $ldap_server,
430
          'ldap_user_conf' => $this,
431
          'direction' => $direction,
432
        );
433

    
434
        drupal_alter('ldap_user_attrs_list', $available_user_attrs[$direction], $params);
435
      }
436
    }
437
    $this->synchMapping = $available_user_attrs;
438

    
439
    cache_set('ldap_user_synch_mapping',  $this->synchMapping);
440
  }
441

    
442
  /**
443
   * given a $prov_event determine if ldap user configuration supports it.
444
   *   this is overall, not per field synching configuration
445
   *
446
   * @param enum $direction LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER or LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY
447
   *
448
   * @param enum $prov_event
449
   *   LDAP_USER_EVENT_SYNCH_TO_DRUPAL_USER, LDAP_USER_EVENT_CREATE_DRUPAL_USER
450
   *   LDAP_USER_EVENT_SYNCH_TO_LDAP_ENTRY LDAP_USER_EVENT_CREATE_LDAP_ENTRY
451
   *   LDAP_USER_EVENT_LDAP_ASSOCIATE_DRUPAL_ACCT
452
   *   LDAP_USER_EVENT_ALL
453
   *
454
   * @param enum $action 'synch', 'provision', 'delete_ldap_entry', 'delete_drupal_entry', 'cancel_drupal_entry'
455
   * @return boolean
456
   */
457

    
458
  public function provisionEnabled($direction, $provision_trigger) {
459
    $result = FALSE;
460

    
461
    if ($direction == LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY) {
462

    
463
      if (!$this->ldapEntryProvisionServer) {
464
        $result = FALSE;
465
      }
466
      else {
467
        $result = in_array($provision_trigger, $this->ldapEntryProvisionTriggers);
468
      }
469

    
470
    }
471
    elseif ($direction == LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER) {
472
      if (!$this->drupalAcctProvisionServer) {
473
        $result = FALSE;
474
      }
475
      else {
476
        $result = in_array($provision_trigger, $this->drupalAcctProvisionTriggers);
477
      }
478
    }
479

    
480
    return $result;
481
  }
482

    
483
 /**
484
   * given a drupal account, provision an ldap entry if none exists.  if one exists do nothing
485
   *
486
   * @param object $account drupal account object with minimum of name property
487
   * @param array $ldap_user as prepopulated ldap entry.  usually not provided
488
   *
489
   * @return array of form:
490
   *     array('status' => 'success', 'fail', or 'conflict'),
491
   *     array('ldap_server' => ldap server object),
492
   *     array('proposed' => proposed ldap entry),
493
   *     array('existing' => existing ldap entry),
494
   *     array('description' = > blah blah)
495
   *
496
   */
497

    
498
  public function provisionLdapEntry($account, $ldap_user = NULL, $test_query = FALSE) {
499
    //debug('provisionLdapEntry account'); //debug($account);
500
    $watchdog_tokens = array();
501
    $result = array(
502
      'status' => NULL,
503
      'ldap_server' => NULL,
504
      'proposed' => NULL,
505
      'existing' => NULL,
506
      'description' => NULL,
507
    );
508

    
509
    if (is_scalar($account)) {
510
      $username = $account;
511
      $account = new stdClass();
512
      $acount->name = $username;
513
    }
514

    
515
    list($account, $user_entity) = ldap_user_load_user_acct_and_entity($account->name);
516

    
517
    if (is_object($account) && property_exists($account, 'uid') && $account->uid == 1) {
518
      $result['status'] = 'fail';
519
      $result['error_description'] = 'can not provision drupal user 1';
520
      return $result; // do not provision or synch user 1
521
    }
522

    
523
    if ($account == FALSE || $account->uid == 0) {
524
      $result['status'] = 'fail';
525
      $result['error_description'] = 'can not provision ldap user unless corresponding drupal account exists first.';
526
      return $result;
527
    }
528

    
529
    if (!$this->ldapEntryProvisionServer || !$this->ldapEntryProvisionServer) {
530
      $result['status'] = 'fail';
531
      $result['error_description'] = 'no provisioning server enabled';
532
      return $result;
533
    }
534

    
535
    $ldap_server = ldap_servers_get_servers($this->ldapEntryProvisionServer, NULL, TRUE);
536
    $params = array(
537
      'direction' => LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY,
538
      'prov_events' => array(LDAP_USER_EVENT_CREATE_LDAP_ENTRY),
539
      'module' => 'ldap_user',
540
      'function' => 'provisionLdapEntry',
541
      'include_count' => FALSE,
542
    );
543

    
544
    list($proposed_ldap_entry, $error) = $this->drupalUserToLdapEntry($account, $ldap_server, $params, $ldap_user);
545
    $proposed_dn = (is_array($proposed_ldap_entry) && isset($proposed_ldap_entry['dn']) && $proposed_ldap_entry['dn']) ? $proposed_ldap_entry['dn'] : NULL;
546
    $proposed_dn_lcase = drupal_strtolower($proposed_dn);
547
    $existing_ldap_entry = ($proposed_dn) ? $ldap_server->dnExists($proposed_dn, 'ldap_entry') : NULL;
548

    
549
    if ($error == LDAP_USER_PROV_RESULT_NO_PWD) {
550
      $result['status'] = 'fail';
551
      $result['description'] = 'Can not provision ldap account without user provided password.';
552
      $result['existing'] = $existing_ldap_entry;
553
      $result['proposed'] = $proposed_ldap_entry;
554
      $result['ldap_server'] = $ldap_server;
555
    }
556
    elseif (!$proposed_dn) {
557
      $result['status'] = 'fail';
558
      $result['description'] = t('failed to derive dn and or mappings');
559
      return $result;
560
    }
561
    elseif ($existing_ldap_entry) {
562
      $result['status'] = 'conflict';
563
      $result['description'] = 'can not provision ldap entry because exists already';
564
      $result['existing'] = $existing_ldap_entry;
565
      $result['proposed'] = $proposed_ldap_entry;
566
      $result['ldap_server'] = $ldap_server;
567
    }
568
    elseif ($test_query) {
569
      $result['status'] = 'fail';
570
      $result['description'] = 'not created because flagged as test query';
571
      $result['proposed'] = $proposed_ldap_entry;
572
      $result['ldap_server'] = $ldap_server;
573
    }
574
    else {
575
      // stick $proposed_ldap_entry in $ldap_entries array for drupal_alter call
576
      $ldap_entries = array($proposed_dn_lcase => $proposed_ldap_entry);
577
      $context = array(
578
        'action' => 'add',
579
        'corresponding_drupal_data' => array($proposed_dn_lcase => $account),
580
        'corresponding_drupal_data_type' => 'user',
581
      );
582
      drupal_alter('ldap_entry_pre_provision', $ldap_entries, $ldap_server, $context);
583
      // remove altered $proposed_ldap_entry from $ldap_entries array
584
      $proposed_ldap_entry = $ldap_entries[$proposed_dn_lcase];
585

    
586
      $ldap_entry_created = $ldap_server->createLdapEntry($proposed_ldap_entry, $proposed_dn);
587
      if ($ldap_entry_created) {
588
        module_invoke_all('ldap_entry_post_provision', $ldap_entries, $ldap_server, $context);
589
        $result['status'] = 'success';
590
        $result['description'] = 'ldap account created';
591
        $result['proposed'] = $proposed_ldap_entry;
592
        $result['created'] = $ldap_entry_created;
593
        $result['ldap_server'] = $ldap_server;
594

    
595
        // need to store <sid>|<dn> in ldap_user_prov_entries field, which may contain more than one
596
        $ldap_user_prov_entry = $ldap_server->sid . '|' . $proposed_ldap_entry['dn'];
597
        if (!isset($user_entity->ldap_user_prov_entries['und'])) {
598
          $user_entity->ldap_user_prov_entries = array('und' => array());
599
        }
600
        $ldap_user_prov_entry_exists = FALSE;
601
        foreach ($user_entity->ldap_user_prov_entries['und'] as $i => $field_value_instance) {
602
          if ($field_value_instance == $ldap_user_prov_entry) {
603
            $ldap_user_prov_entry_exists = TRUE;
604
          }
605
        }
606
        if (!$ldap_user_prov_entry_exists) {
607
          $user_entity->ldap_user_prov_entries['und'][] = array(
608
            'value' =>  $ldap_user_prov_entry,
609
            'format' => NULL,
610
            'save_value' => $ldap_user_prov_entry,
611
          );
612
          $edit = array(
613
            'ldap_user_prov_entries' => $user_entity->ldap_user_prov_entries,
614
          );
615
          $account = user_load($account->uid);
616
          $account = user_save($account, $edit);
617
        }
618

    
619
      }
620
      else {
621
        $result['status'] = 'fail';
622
        $result['proposed'] = $proposed_ldap_entry;
623
        $result['created'] = $ldap_entry_created;
624
        $result['ldap_server'] = $ldap_server;
625
        $result['existing'] = NULL;
626
      }
627
    }
628

    
629
    $tokens = array(
630
      '%dn' => isset($result['proposed']['dn']) ? $result['proposed']['dn'] : NULL,
631
      '%sid' => (isset($result['ldap_server']) && $result['ldap_server']) ? $result['ldap_server']->sid : 0,
632
      '%username' => @$account->name,
633
      '%uid' => @$account->uid,
634
      '%description' => @$result['description'],
635
    );
636
    if (!$test_query && isset($result['status'])) {
637
      if ($result['status'] == 'success') {
638
        if ($this->detailedWatchdog) {
639
          watchdog('ldap_user', 'LDAP entry on server %sid created dn=%dn.  %description. username=%username, uid=%uid', $tokens, WATCHDOG_INFO);
640
        }
641
      }
642
      elseif ($result['status'] == 'conflict') {
643
        if ($this->detailedWatchdog) {
644
          watchdog('ldap_user', 'LDAP entry on server %sid not created because of existing ldap entry. %description. username=%username, uid=%uid', $tokens, WATCHDOG_WARNING);
645
        }
646
      }
647
      elseif ($result['status'] == 'fail') {
648
        watchdog('ldap_user', 'LDAP entry on server %sid not created because error.  %description. username=%username, uid=%uid', $tokens, WATCHDOG_ERROR);
649
      }
650
    }
651
    return $result;
652
  }
653

    
654

    
655
  /**
656
   * given a drupal account, synch to related ldap entry
657
   *
658
   * @param drupal user object $account.  Drupal user object
659
   * @param array $user_edit.  Edit array for user_save.  generally null unless user account is being created or modified in same synching
660
   * @param array $ldap_user.  current ldap data of user. @see README.developers.txt for structure
661
   *
662
   * @return TRUE on success or FALSE on fail.
663
   */
664

    
665
  public function synchToLdapEntry($account, $user_edit = NULL, $ldap_user =  array(), $test_query = FALSE) {
666

    
667
    if (is_object($account) && property_exists($account, 'uid') && $account->uid == 1) {
668
      return FALSE; // do not provision or synch user 1
669
    }
670

    
671
    $watchdog_tokens = array();
672
    $result = FALSE;
673
    $proposed_ldap_entry = FALSE;
674

    
675
    if ($this->ldapEntryProvisionServer) {
676
      $ldap_server = ldap_servers_get_servers($this->ldapEntryProvisionServer, NULL, TRUE);
677

    
678
      $params = array(
679
        'direction' => LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY,
680
        'prov_events' => array(LDAP_USER_EVENT_SYNCH_TO_LDAP_ENTRY),
681
        'module' => 'ldap_user',
682
        'function' => 'synchToLdapEntry',
683
        'include_count' => FALSE,
684
      );
685

    
686
      list($proposed_ldap_entry, $error) = $this->drupalUserToLdapEntry($account, $ldap_server, $params, $ldap_user);
687
      if ($error != LDAP_USER_PROV_RESULT_NO_ERROR) {
688
        $result = FALSE;
689
      }
690
      elseif (is_array($proposed_ldap_entry) && isset($proposed_ldap_entry['dn'])) {
691
        $existing_ldap_entry = $ldap_server->dnExists($proposed_ldap_entry['dn'], 'ldap_entry');
692
        $attributes = array(); // this array represents attributes to be modified; not comprehensive list of attributes
693
        foreach ($proposed_ldap_entry as $attr_name => $attr_values) {
694
          if ($attr_name != 'dn') {
695
            if (isset($attr_values['count'])) {
696
              unset($attr_values['count']);
697
            }
698
            if (count($attr_values) == 1) {
699
              $attributes[$attr_name] = $attr_values[0];
700
            }
701
            else {
702
              $attributes[$attr_name] = $attr_values;
703
            }
704
          }
705
        }
706

    
707
        if ($test_query) {
708
          $proposed_ldap_entry = $attributes;
709
          $result = array(
710
            'proposed' => $proposed_ldap_entry,
711
            'server' => $ldap_server,
712
          );
713
        }
714
        else {
715
         //  //debug('modifyLdapEntry,dn=' . $proposed_ldap_entry['dn']);  //debug($attributes);
716
              // stick $proposed_ldap_entry in $ldap_entries array for drupal_alter call
717
          $proposed_dn_lcase = drupal_strtolower($proposed_ldap_entry['dn']);
718
          $ldap_entries = array($proposed_dn_lcase => $attributes);
719
          $context = array(
720
            'action' => 'update',
721
            'corresponding_drupal_data' => array($proposed_dn_lcase => $attributes),
722
            'corresponding_drupal_data_type' => 'user',
723
          );
724
          drupal_alter('ldap_entry_pre_provision', $ldap_entries, $ldap_server, $context);
725
          // remove altered $proposed_ldap_entry from $ldap_entries array
726
          $attributes = $ldap_entries[$proposed_dn_lcase];
727
          $result = $ldap_server->modifyLdapEntry($proposed_ldap_entry['dn'], $attributes);
728
          if ($result) { // success
729
            module_invoke_all('ldap_entry_post_provision', $ldap_entries, $ldap_server, $context);
730
          }
731
        }
732
      }
733
      else { // failed to get acceptable proposed ldap entry
734
        $result = FALSE;
735
      }
736
    }
737

    
738
    $tokens = array(
739
      '%dn' => isset($proposed_ldap_entry['dn']) ? $proposed_ldap_entry['dn'] : NULL,
740
      '%sid' => $this->ldapEntryProvisionServer,
741
      '%username' => $account->name,
742
      '%uid' => ($test_query || !property_exists($account, 'uid')) ? '' : $account->uid,
743
    );
744

    
745
    if ($result) {
746
      watchdog('ldap_user', 'LDAP entry on server %sid synched dn=%dn. username=%username, uid=%uid', $tokens, WATCHDOG_INFO);
747
    }
748
    else {
749
      watchdog('ldap_user', 'LDAP entry on server %sid not synched because error. username=%username, uid=%uid', $tokens, WATCHDOG_ERROR);
750
    }
751

    
752
    return $result;
753

    
754
  }
755

    
756
  /**
757
   * given a drupal account, query ldap and get all user fields and create user account
758
   *
759
   * @param array $account drupal account array with minimum of name
760
   * @param array $user_edit drupal edit array in form user_save($account, $user_edit) would take,
761
   *   generally empty unless overriding synchToDrupalAccount derived values
762
   * @param array $ldap_user as user's ldap entry.  passed to avoid requerying ldap in cases where already present
763
   * @param boolean $save indicating if drupal user should be saved.  generally depends on where function is called from.
764
   *
765
   * @return result of user_save() function is $save is true, otherwise return TRUE
766
   *   $user_edit data returned by reference
767
   *
768
   */
769
  public function synchToDrupalAccount($drupal_user, &$user_edit, $prov_event = LDAP_USER_EVENT_SYNCH_TO_DRUPAL_USER, $ldap_user = NULL,  $save = FALSE) {
770
    
771
    $debug = array(
772
      'account' => $drupal_user,
773
      'user_edit' => $user_edit,
774
      'ldap_user' => $ldap_user,
775
    );
776

    
777
    if (
778
        (!$ldap_user  && !isset($drupal_user->name)) ||
779
        (!$drupal_user && $save) ||
780
        ($ldap_user && !isset($ldap_user['sid']))
781
    ) {
782
       // should throw watchdog error also
783
      return FALSE;
784
    }
785

    
786
    if (!$ldap_user && $this->drupalAcctProvisionServer) {
787
      $ldap_user = ldap_servers_get_user_ldap_data($drupal_user->name, $this->drupalAcctProvisionServer, 'ldap_user_prov_to_drupal');
788
    }
789

    
790
    if (!$ldap_user) {
791
      return FALSE;
792
    }
793

    
794
    if ($this->drupalAcctProvisionServer) {
795
      $ldap_server = ldap_servers_get_servers($this->drupalAcctProvisionServer, NULL, TRUE);
796
      $this->entryToUserEdit($ldap_user, $user_edit, $ldap_server, LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER, array($prov_event));
797
    }
798

    
799
    if ($save) {
800
      $account = user_load($drupal_user->uid);
801
      $result = user_save($account, $user_edit, 'ldap_user');
802
      return $result;
803
    }
804
    else {
805
      return TRUE;
806
    }
807
  }
808

    
809

    
810
  /**
811
   * given a drupal account, delete user account
812
   *
813
   * @param string $username drupal account name
814
   * @return TRUE or FALSE.  FALSE indicates failed or action not enabled in ldap user configuration
815
   */
816
  public function deleteDrupalAccount($username) {
817
    $user = user_load_by_name($username);
818
    if (is_object($user)) {
819
      user_delete($user->uid);
820
      return TRUE;
821
    }
822
    else {
823
      return FALSE;
824
    }
825
  }
826

    
827
  /**
828
   * given a drupal account, find the related ldap entry.
829
   *
830
   * @param drupal user object $account
831
   *
832
   * @return FALSE or ldap entry
833
   */
834
  public function getProvisionRelatedLdapEntry($account, $prov_events = NULL) {
835
    if (!$prov_events) {
836
      $prov_events = ldap_user_all_events();
837
    }
838
    $sid = $this->ldapEntryProvisionServer; //
839
    //debug("ldapEntryProvisionServer:$sid");
840
    if (!$sid) {
841
      return FALSE;
842
    }
843
    // $user_entity->ldap_user_prov_entries,
844
    $ldap_server = ldap_servers_get_servers($sid, NULL, TRUE);
845
    $params = array(
846
      'direction' => LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY,
847
      'prov_events' => $prov_events,
848
      'module' => 'ldap_user',
849
      'function' => 'getProvisionRelatedLdapEntry',
850
      'include_count' => FALSE,
851
      );
852
    list($proposed_ldap_entry, $error) = $this->drupalUserToLdapEntry($account, $ldap_server, $params);
853
    if (!(is_array($proposed_ldap_entry) && isset($proposed_ldap_entry['dn']) && $proposed_ldap_entry['dn'])) {
854
      return FALSE;
855
    }
856
    $ldap_entry = $ldap_server->dnExists($proposed_ldap_entry['dn'], 'ldap_entry', array());
857
    return $ldap_entry;
858

    
859
  }
860

    
861
  /**
862
   * given a drupal account, delete ldap entry that was provisioned based on it
863
   *   normally this will be 0 or 1 entry, but the ldap_user_provisioned_ldap_entries
864
   *   field attached to the user entity track each ldap entry provisioned
865
   *
866
   * @param object $account drupal account
867
   * @return TRUE or FALSE.  FALSE indicates failed or action not enabled in ldap user configuration
868
   */
869
  public function deleteProvisionedLdapEntries($account) {
870
    // determine server that is associated with user
871

    
872
    $boolean_result = FALSE;
873
    $language = ($account->language) ? $account->language : 'und';
874
    if (isset($account->ldap_user_prov_entries[$language][0])) {
875
      foreach ($account->ldap_user_prov_entries[$language] as $i => $field_instance) {
876
        $parts = explode('|', $field_instance['value']);
877
        if (count($parts) == 2) {
878

    
879
          list($sid, $dn) = $parts;
880
          $ldap_server = ldap_servers_get_servers($sid, NULL, TRUE);
881
          if (is_object($ldap_server) && $dn) {
882
            $boolean_result = $ldap_server->delete($dn);
883
            $tokens = array('%sid' => $sid, '%dn' => $dn, '%username' => $account->name, '%uid' => $account->uid);
884
            if ($boolean_result) {
885
              watchdog('ldap_user', 'LDAP entry on server %sid deleted dn=%dn. username=%username, uid=%uid', $tokens, WATCHDOG_INFO);
886
            }
887
            else {
888
              watchdog('ldap_user', 'LDAP entry on server %sid not deleted because error. username=%username, uid=%uid', $tokens, WATCHDOG_ERROR);
889
            }
890
          }
891
          else {
892
            $boolean_result = FALSE;
893
          }
894
        }
895
      }
896
    }
897
    return $boolean_result;
898

    
899
  }
900

    
901
/**
902
  *  populate ldap entry array for provisioning
903
  *
904
  * @param array $account drupal account
905
  * @param object $ldap_server
906
  * @param array $ldap_user ldap entry of user, returned by reference
907
  * @param array $params with the following key values:
908
  *    'ldap_context' =>
909
       'module' => module calling function, e.g. 'ldap_user'
910
       'function' => function calling function, e.g. 'provisionLdapEntry'
911
       'include_count' => should 'count' array key be included
912
       'direction' => LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY || LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER
913
  *
914
  * @return array(ldap entry, $result) in ldap extension array format.!THIS IS NOT THE ACTUAL LDAP ENTRY
915
  */
916

    
917
  function drupalUserToLdapEntry($account, $ldap_server, $params, $ldap_user_entry = NULL) {
918
  //debug('call to drupalUserToLdapEntry, account:'); //debug($account); //debug('ldap_server'); //debug($ldap_server);
919
  //debug('params'); //debug($params); //debug('ldap_user_entry');//debug($ldap_user_entry);
920
    $provision = (isset($params['function']) && $params['function'] == 'provisionLdapEntry');
921
    $result = LDAP_USER_PROV_RESULT_NO_ERROR;
922
    if (!$ldap_user_entry) {
923
      $ldap_user_entry = array();
924
    }
925

    
926
    if (!is_object($account) || !is_object($ldap_server)) {
927
      return array(NULL, LDAP_USER_PROV_RESULT_BAD_PARAMS);
928
    }
929
    $watchdog_tokens = array(
930
      '%drupal_username' => $account->name,
931
    );
932
    $include_count = (isset($params['include_count']) && $params['include_count']);
933

    
934
    $direction = isset($params['direction']) ? $params['direction'] : LDAP_USER_PROV_DIRECTION_ALL;
935
    $prov_events = empty($params['prov_events']) ? ldap_user_all_events() : $params['prov_events'];
936

    
937
    $mappings = $this->getSynchMappings($direction, $prov_events);
938
     //debug('prov_events'); //debug(join(",",$prov_events));
939
  //  debug('mappings'); debug($mappings);
940
      // Loop over the mappings.
941
    foreach ($mappings as $field_key => $field_detail) {
942
      list($ldap_attr_name, $ordinal, $conversion) = ldap_servers_token_extract_parts($field_key, TRUE);  //trim($field_key, '[]');
943
      $ordinal = (!$ordinal) ? 0 : $ordinal;
944
      if ($ldap_user_entry && isset($ldap_user_entry[$ldap_attr_name]) && is_array($ldap_user_entry[$ldap_attr_name]) && isset($ldap_user_entry[$ldap_attr_name][$ordinal]) ) {
945
        continue; // don't override values passed in;
946
      }
947

    
948
      $synched = $this->isSynched($field_key, $params['prov_events'], LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY);
949
    //  debug("isSynched $field_key: $synched");
950
      if ($synched) {
951
        $token = ($field_detail['user_attr'] == 'user_tokens') ? $field_detail['user_tokens'] : $field_detail['user_attr'];
952
        $value = ldap_servers_token_replace($account, $token, 'user_account');
953

    
954
        if (substr($token, 0, 10) == '[password.' && (!$value || $value == $token)) { // deal with empty/unresolved password
955
          if (!$provision) {
956
            continue; //don't overwrite password on synch if no value provided
957
          }
958
        }
959

    
960
        if ($ldap_attr_name == 'dn' && $value) {
961
          $ldap_user_entry['dn'] = $value;
962
        }
963
        elseif ($value) {
964
          if (!isset($ldap_user_entry[$ldap_attr_name]) || !is_array($ldap_user_entry[$ldap_attr_name])) {
965
            $ldap_user_entry[$ldap_attr_name] = array();
966
          }
967
          $ldap_user_entry[$ldap_attr_name][$ordinal] = $value;
968
          if ($include_count) {
969
            $ldap_user_entry[$ldap_attr_name]['count'] = count($ldap_user_entry[$ldap_attr_name]);
970
          }
971

    
972
        }
973

    
974
      }
975

    
976
    }
977

    
978
    /**
979
     * 4. call drupal_alter() to allow other modules to alter $ldap_user
980
     */
981

    
982
    drupal_alter('ldap_entry', $ldap_user_entry, $params);
983

    
984
    return array($ldap_user_entry, $result);
985

    
986
  }
987

    
988

    
989

    
990
   /**
991
   * given a drupal account, query ldap and get all user fields and save user account
992
   * (note: parameters are in odd order to match synchDrupalAccount handle)
993
   *
994
   * @param array $account drupal account object or null
995
   * @param array $user_edit drupal edit array in form user_save($account, $user_edit) would take.
996
   * @param array $ldap_user as user's ldap entry.  passed to avoid requerying ldap in cases where already present
997
   * @param boolean $save indicating if drupal user should be saved.  generally depends on where function is called from and if the
998
   *
999
   * @return result of user_save() function is $save is true, otherwise return TRUE on success or FALSE on any problem
1000
   *   $user_edit data returned by reference
1001
   *
1002
   */
1003

    
1004
  public function provisionDrupalAccount($account = FALSE, &$user_edit, $ldap_user = NULL, $save = TRUE) {
1005

    
1006
    $watchdog_tokens = array();
1007
    /**
1008
     * @todo
1009
     * -- add error catching for conflicts, conflicts should be checked before calling this function.
1010
     *
1011
     */
1012

    
1013
    if (!$account) {
1014
      $account = new stdClass();
1015
    }
1016
    $account->is_new = TRUE;
1017

    
1018
    if (!$ldap_user && !isset($user_edit['name'])) {
1019
      return FALSE;
1020
    }
1021

    
1022
    if (!$ldap_user) {
1023
      $watchdog_tokens['%username'] = $user_edit['name'];
1024
      if ($this->drupalAcctProvisionServer) {
1025
        $ldap_user = ldap_servers_get_user_ldap_data($user_edit['name'], $this->drupalAcctProvisionServer, 'ldap_user_prov_to_drupal');
1026
      }
1027
      if (!$ldap_user) {
1028
        if ($this->detailedWatchdog) {
1029
          watchdog('ldap_user', '%username : failed to find associated ldap entry for username in provision.', $watchdog_tokens, WATCHDOG_DEBUG);
1030
        }
1031
        return FALSE;
1032
      }
1033
    }
1034
   // dpm('ldap_user 675');dpm($ldap_user);
1035
    if (!isset($user_edit['name']) && isset($account->name)) {
1036
      $user_edit['name'] = $account->name;
1037
      $watchdog_tokens['%username'] = $user_edit['name'];
1038
    }
1039
    //When using the multi-domain last authentication option
1040
    //$ldap_server breaks beacause $this->drupalAcctProvisionServer is set on LDAP_USER_AUTH_SERVER_SID
1041
    //So we need to check it's not the case before using ldap_servers_get_servers
1042
    if ($this->drupalAcctProvisionServer && $this->drupalAcctProvisionServer != LDAP_USER_AUTH_SERVER_SID) {
1043

    
1044
      $ldap_server = ldap_servers_get_servers($this->drupalAcctProvisionServer, 'enabled', TRUE);  // $ldap_user['sid']
1045

    
1046
      $params = array(
1047
        'account' => $account,
1048
        'user_edit' => $user_edit,
1049
        'prov_event' => LDAP_USER_EVENT_CREATE_DRUPAL_USER,
1050
        'module' => 'ldap_user',
1051
        'function' => 'provisionDrupalAccount',
1052
        'direction' => LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER,
1053
      );
1054

    
1055
      drupal_alter('ldap_entry', $ldap_user, $params);
1056

    
1057
      // look for existing drupal account with same puid.  if so update username and attempt to synch in current context
1058
      $puid = $ldap_server->userPuidFromLdapEntry($ldap_user['attr']);
1059
      $account2 = ($puid) ? $ldap_server->userUserEntityFromPuid($puid) : FALSE;
1060

    
1061
      if ($account2) { // synch drupal account, since drupal account exists
1062
        // 1. correct username and authmap
1063
        $this->entryToUserEdit($ldap_user, $user_edit, $ldap_server, LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER, array(LDAP_USER_EVENT_SYNCH_TO_DRUPAL_USER));
1064
        $account = user_save($account2, $user_edit, 'ldap_user');
1065
        user_set_authmaps($account, array("authname_ldap_user" => $user_edit['name']));
1066
        // 2. attempt synch if appropriate for current context
1067
        if ($account) {
1068
          $account = $this->synchToDrupalAccount($account, $user_edit, LDAP_USER_EVENT_SYNCH_TO_DRUPAL_USER, $ldap_user, TRUE);
1069
        }
1070
        return $account;
1071
      }
1072
      else { // create drupal account
1073
        $this->entryToUserEdit($ldap_user, $user_edit, $ldap_server, LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER, array(LDAP_USER_EVENT_CREATE_DRUPAL_USER));
1074
        if ($save) {
1075
          $watchdog_tokens = array('%drupal_username' =>  $user_edit['name']);
1076
          if (empty($user_edit['name'])) {
1077
            drupal_set_message(t('User account creation failed because of invalid, empty derived Drupal username.'), 'error');
1078
            watchdog('ldap_user',
1079
              'Failed to create Drupal account %drupal_username because drupal username could not be derived.',
1080
              $watchdog_tokens,
1081
              WATCHDOG_ERROR
1082
            );
1083
            return FALSE;
1084
          }
1085
          if (!isset($user_edit['mail']) || !$user_edit['mail']) {
1086
            drupal_set_message(t('User account creation failed because of invalid, empty derived email address.'), 'error');
1087
            watchdog('ldap_user',
1088
              'Failed to create Drupal account %drupal_username because email address could not be derived by LDAP User module',
1089
              $watchdog_tokens,
1090
              WATCHDOG_ERROR
1091
            );
1092
            return FALSE;
1093
          }
1094
          if ($account_with_same_email = user_load_by_mail($user_edit['mail'])) {
1095
            $watchdog_tokens['%email'] = $user_edit['mail'];
1096
            $watchdog_tokens['%duplicate_name'] = $account_with_same_email->name;
1097
            watchdog('ldap_user', 'LDAP user %drupal_username has email address
1098
              (%email) conflict with a drupal user %duplicate_name', $watchdog_tokens, WATCHDOG_ERROR);
1099
            drupal_set_message(t('Another user already exists in the system with the same email address. You should contact the system administrator in order to solve this conflict.'), 'error');
1100
            return FALSE;
1101
          }
1102
          $account = user_save(NULL, $user_edit, 'ldap_user');
1103
          if (!$account) {
1104
            drupal_set_message(t('User account creation failed because of system problems.'), 'error');
1105
          }
1106
          else {
1107
            user_set_authmaps($account, array('authname_ldap_user' => $account->name));
1108
            ldap_user_ldap_provision_semaphore('drupal_created', 'set', $account->name);
1109
          }
1110
          return $account;
1111
        }
1112
        return TRUE;
1113
      }
1114
    }
1115
  }
1116

    
1117
  /**
1118
   * set ldap associations of a drupal account by altering user fields
1119
   *
1120
   * @param string $drupal_username
1121
   *
1122
   * @return boolean TRUE on success, FALSE on error or failure because of invalid user or ldap accounts
1123
   *
1124
   */
1125
  function ldapAssociateDrupalAccount($drupal_username) {
1126

    
1127
    if ($this->drupalAcctProvisionServer) {
1128
      $prov_events = array(LDAP_USER_EVENT_LDAP_ASSOCIATE_DRUPAL_ACCT);
1129
      $ldap_server = ldap_servers_get_servers($this->drupalAcctProvisionServer, 'enabled', TRUE);  // $ldap_user['sid']
1130
      $account = user_load_by_name($drupal_username);
1131
      $ldap_user = ldap_servers_get_user_ldap_data($drupal_username, $this->drupalAcctProvisionServer, 'ldap_user_prov_to_drupal');
1132
      if (!$account) {
1133
        watchdog(
1134
          'ldap_user',
1135
          'Failed to LDAP associate drupal account %drupal_username because account not found',
1136
          array('%drupal_username' => $drupal_username),
1137
          WATCHDOG_ERROR
1138
        );
1139
        return FALSE;
1140
      }
1141
      elseif (!$ldap_user) {
1142
        watchdog(
1143
          'ldap_user',
1144
          'Failed to LDAP associate drupal account %drupal_username because corresponding LDAP entry not found',
1145
          array('%drupal_username' => $drupal_username),
1146
          WATCHDOG_ERROR
1147
        );
1148
        return FALSE;
1149
      }
1150
      else {
1151
        $user_edit = array();
1152
        $user_edit['data']['ldap_user']['init'] = array(
1153
          'sid'  => $ldap_user['sid'],
1154
          'dn'   => $ldap_user['dn'],
1155
          'mail'   => $account->mail,
1156
        );
1157
        $ldap_user_puid = $ldap_server->userPuidFromLdapEntry($ldap_user['attr']);
1158
        if ($ldap_user_puid) {
1159
          $user_edit['ldap_user_puid'][LANGUAGE_NONE][0]['value'] = $ldap_user_puid; //
1160
        }
1161
        $user_edit['ldap_user_puid_property'][LANGUAGE_NONE][0]['value'] = $ldap_server->unique_persistent_attr;
1162
        $user_edit['ldap_user_puid_sid'][LANGUAGE_NONE][0]['value'] = $ldap_server->sid;
1163
        $user_edit['ldap_user_current_dn'][LANGUAGE_NONE][0]['value'] = $ldap_user['dn'];
1164
        $account = user_save($account, $user_edit, 'ldap_user');
1165
        return (boolean)$account;
1166
      }
1167
    }
1168
    else {
1169
      return FALSE;
1170
    }
1171
  }
1172

    
1173
  /** populate $user edit array (used in hook_user_save, hook_user_update, etc)
1174
   * ... should not assume all attribues are present in ldap entry
1175
   *
1176
   * @param array ldap entry $ldap_user
1177
   * @param array $edit see hook_user_save, hook_user_update, etc
1178
   * @param object $ldap_server
1179
   * @param enum $direction
1180
   * @param array $prov_events
1181
   *
1182
   */
1183

    
1184
  function entryToUserEdit($ldap_user, &$edit, $ldap_server, $direction = LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER, $prov_events = NULL) {
1185

    
1186
    // need array of user fields and which direction and when they should be synched.
1187
    if (!$prov_events) {
1188
      $prov_events = ldap_user_all_events();
1189
    }
1190
    $mail_synched = $this->isSynched('[property.mail]', $prov_events, $direction);
1191
    if (!isset($edit['mail']) && $mail_synched) {
1192
      $derived_mail = $ldap_server->userEmailFromLdapEntry($ldap_user['attr']);
1193
      if ($derived_mail) {
1194
        $edit['mail'] = $derived_mail;
1195
      }
1196
    }
1197

    
1198
    $drupal_username = $ldap_server->userUsernameFromLdapEntry($ldap_user['attr']);
1199
                if ($this->isSynched('[property.picture]', $prov_events, $direction)){
1200

    
1201
                        $picture = $ldap_server->userPictureFromLdapEntry($ldap_user['attr'], $drupal_username);
1202

    
1203
                        if ($picture){
1204
                                $edit['picture'] = $picture;
1205
                                if(isset($picture->md5Sum)){
1206
                                        $edit['data']['ldap_user']['init']['thumb5md'] = $picture->md5Sum;
1207
                                }
1208
                        }
1209
                }
1210

    
1211
    if ($this->isSynched('[property.name]', $prov_events, $direction) && !isset($edit['name']) && $drupal_username) {
1212
      $edit['name'] = $drupal_username;
1213
    }
1214

    
1215
    if ($direction == LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER && in_array(LDAP_USER_EVENT_CREATE_DRUPAL_USER, $prov_events)) {
1216
      $edit['mail'] = isset($edit['mail']) ? $edit['mail'] : $ldap_user['mail'];
1217
      if (!isset($edit['pass'])) {
1218
        $edit['pass'] = user_password(20);
1219
        watchdog('ldap_user', '20 character random password generated for the %username account that has been created.', array('%username' => $drupal_username), WATCHDOG_INFO);
1220
      }
1221
      $edit['init'] = isset($edit['init']) ? $edit['init'] : $edit['mail'];
1222
      $edit['status'] = isset($edit['status']) ? $edit['status'] : 1;
1223
      $edit['signature'] = isset($edit['signature']) ? $edit['signature'] : '';
1224

    
1225
      $edit['data']['ldap_user']['init'] = array(
1226
        'sid'  => $ldap_user['sid'],
1227
        'dn'   => $ldap_user['dn'],
1228
        'mail' => $edit['mail'],
1229
      );
1230
    }
1231

    
1232
    /*
1233
     * Make sure the user account has the latest ldap_user settings
1234
     * when syncing the profile.
1235
     */
1236
    if ($direction == LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER && in_array(LDAP_USER_EVENT_SYNCH_TO_DRUPAL_USER, $prov_events)) {
1237
       $edit['data']['ldap_user']['init'] = array(
1238
        'sid'  => $ldap_user['sid'],
1239
        'dn'   => $ldap_user['dn'],
1240
        'mail' => isset($edit['mail']) && !empty($edit['mail']) ? $edit['mail'] : $ldap_user['mail'],
1241
      );
1242
    }
1243

    
1244
    /**
1245
     * basic $user ldap fields
1246
     */
1247
    if ($this->isSynched('[field.ldap_user_puid]', $prov_events, $direction)) {
1248
      $ldap_user_puid = $ldap_server->userPuidFromLdapEntry($ldap_user['attr']);
1249
      if ($ldap_user_puid) {
1250
        $edit['ldap_user_puid'][LANGUAGE_NONE][0]['value'] = $ldap_user_puid; //
1251
      }
1252
    }
1253
    if ($this->isSynched('[field.ldap_user_puid_property]', $prov_events, $direction)) {
1254
      $edit['ldap_user_puid_property'][LANGUAGE_NONE][0]['value'] = $ldap_server->unique_persistent_attr;
1255
    }
1256
    if ($this->isSynched('[field.ldap_user_puid_sid]', $prov_events, $direction)) {
1257
      $edit['ldap_user_puid_sid'][LANGUAGE_NONE][0]['value'] = $ldap_server->sid;
1258
    }
1259
    if ($this->isSynched('[field.ldap_user_current_dn]', $prov_events, $direction)) {
1260
      $edit['ldap_user_current_dn'][LANGUAGE_NONE][0]['value'] = $ldap_user['dn'];
1261
    }
1262

    
1263
    // Get any additional mappings.
1264
    $mappings = $this->getSynchMappings($direction, $prov_events);
1265

    
1266
     // Loop over the mappings.
1267
    foreach ($mappings as $user_attr_key => $field_detail) {
1268

    
1269
       // Make sure this mapping is relevant to the sync context.
1270
      if (!$this->isSynched($user_attr_key, $prov_events, $direction)) {
1271
        continue;
1272
      }
1273
       /**
1274
        * if "convert from binary is selected" and no particular method is in token,
1275
        * default to ldap_servers_binary() function
1276
        */
1277
      if ($field_detail['convert'] && strpos($field_detail['ldap_attr'], ';') === FALSE) {
1278
        $field_detail['ldap_attr'] = str_replace(']', ';binary]', $field_detail['ldap_attr']);
1279
      }
1280
      $value = ldap_servers_token_replace($ldap_user['attr'], $field_detail['ldap_attr'], 'ldap_entry');
1281
      list($value_type, $value_name, $value_instance) = ldap_servers_parse_user_attr_name($user_attr_key);
1282

    
1283
      // $value_instance not used, may have future use case
1284

    
1285
      // Are we dealing with a field?
1286
      if ($value_type == 'field') {
1287
        // Field api field - first we get the field.
1288
        $field = field_info_field($value_name);
1289
        // Then the columns for the field in the schema.
1290
        $columns = array_keys($field['columns']);
1291
        // Then we convert the value into an array if it's scalar.
1292
        $values = $field['cardinality'] == 1 ? array($value) : (array) $value;
1293

    
1294
        $items = array();
1295
        // Loop over the values and set them in our $items array.
1296
        foreach ($values as $delta => $value) {
1297
          if (isset($value)) {
1298
            // We set the first column value only, this is consistent with
1299
            // the Entity Api (@see entity_metadata_field_property_set).
1300
            $items[$delta][$columns[0]] = $value;
1301
          }
1302
        }
1303
        // Add them to our edited item.
1304
        $edit[$value_name][LANGUAGE_NONE] = $items;
1305
      }
1306
      elseif ($value_type == 'property') {
1307
        // Straight property.
1308
        $edit[$value_name] = $value;
1309
      }
1310
    }
1311

    
1312
    // Allow other modules to have a say.
1313

    
1314
    drupal_alter('ldap_user_edit_user', $edit, $ldap_user, $ldap_server, $prov_events);
1315
    if (isset($edit['name']) && $edit['name'] == '') {  // don't let empty 'name' value pass for user
1316
      unset($edit['name']);
1317
    }
1318

    
1319
  }
1320
  /**
1321
   * given configuration of synching, determine is a given synch should occur
1322
   *
1323
   * @param string $attr_token e.g. [property.mail], [field.ldap_user_puid_property]
1324
   * @param object $ldap_server
1325
   * @param array $prov_events e.g. array(LDAP_USER_EVENT_CREATE_DRUPAL_USER).  typically array with 1 element
1326
   * @param scalar $direction LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER or LDAP_USER_PROV_DIRECTION_TO_LDAP_ENTRY
1327
   */
1328

    
1329
  public function isSynched($attr_token, $prov_events, $direction) {
1330
    $result = (boolean)(
1331
      isset($this->synchMapping[$direction][$attr_token]['prov_events']) &&
1332
      count(array_intersect($prov_events, $this->synchMapping[$direction][$attr_token]['prov_events']))
1333
    );
1334
    if (!$result) {
1335
      if (isset($this->synchMapping[$direction][$attr_token])) {
1336
        //debug($this->synchMapping[$direction][$attr_token]);
1337
      }
1338
      else {
1339
      //  debug("$attr_token not in ldapUserConf::synchMapping");
1340
      }
1341
    }
1342
    return $result;
1343
  }
1344

    
1345

    
1346
} // end LdapUserConf class