Projet

Général

Profil

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

root / drupal7 / sites / all / modules / ldap / ldap_user / LdapUserConf.class.php @ 7547bb19

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[LANGUAGE_NONE])) {
598
          $user_entity->ldap_user_prov_entries = array(LANGUAGE_NONE => array());
599
        }
600
        $ldap_user_prov_entry_exists = FALSE;
601
        foreach ($user_entity->ldap_user_prov_entries[LANGUAGE_NONE] 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[LANGUAGE_NONE][] = array(
608
            'value' =>  $ldap_user_prov_entry,
609
          );
610

    
611
          // Save the field without calling user_save()
612
          field_attach_presave('user', $user_entity);
613
          field_attach_update('user', $user_entity);
614
        }
615

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

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

    
651

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

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

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

    
668
    $watchdog_tokens = array();
669
    $result = FALSE;
670
    $proposed_ldap_entry = FALSE;
671

    
672
    if ($this->ldapEntryProvisionServer) {
673
      $ldap_server = ldap_servers_get_servers($this->ldapEntryProvisionServer, NULL, TRUE);
674

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

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

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

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

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

    
749
    return $result;
750

    
751
  }
752

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

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

    
783
    if (!$ldap_user && $this->drupalAcctProvisionServer) {
784
      $ldap_user = ldap_servers_get_user_ldap_data($drupal_user->name, $this->drupalAcctProvisionServer, 'ldap_user_prov_to_drupal');
785
    }
786

    
787
    if (!$ldap_user) {
788
      return FALSE;
789
    }
790

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

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

    
806

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

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

    
856
  }
857

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

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

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

    
896
  }
897

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

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

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

    
931
    $direction = isset($params['direction']) ? $params['direction'] : LDAP_USER_PROV_DIRECTION_ALL;
932
    $prov_events = empty($params['prov_events']) ? ldap_user_all_events() : $params['prov_events'];
933

    
934
    $mappings = $this->getSynchMappings($direction, $prov_events);
935
     //debug('prov_events'); //debug(join(",",$prov_events));
936
  //  debug('mappings'); debug($mappings);
937
      // Loop over the mappings.
938
    foreach ($mappings as $field_key => $field_detail) {
939
      list($ldap_attr_name, $ordinal, $conversion) = ldap_servers_token_extract_parts($field_key, TRUE);  //trim($field_key, '[]');
940
      $ordinal = (!$ordinal) ? 0 : $ordinal;
941
      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]) ) {
942
        continue; // don't override values passed in;
943
      }
944

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

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

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

    
969
        }
970

    
971
      }
972

    
973
    }
974

    
975
    /**
976
     * 4. call drupal_alter() to allow other modules to alter $ldap_user
977
     */
978

    
979
    drupal_alter('ldap_entry', $ldap_user_entry, $params);
980

    
981
    return array($ldap_user_entry, $result);
982

    
983
  }
984

    
985

    
986

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

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

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

    
1010
    if (!$account) {
1011
      $account = new stdClass();
1012
    }
1013
    $account->is_new = TRUE;
1014

    
1015
    if (!$ldap_user && !isset($user_edit['name'])) {
1016
      return FALSE;
1017
    }
1018

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

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

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

    
1052
      drupal_alter('ldap_entry', $ldap_user, $params);
1053

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

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

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

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

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

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

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

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

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

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

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

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

    
1222
      $edit['data']['ldap_user']['init'] = array(
1223
        'sid'  => $ldap_user['sid'],
1224
        'dn'   => $ldap_user['dn'],
1225
        'mail' => $edit['mail'],
1226
      );
1227
    }
1228

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

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

    
1260
    // Get any additional mappings.
1261
    $mappings = $this->getSynchMappings($direction, $prov_events);
1262

    
1263
     // Loop over the mappings.
1264
    foreach ($mappings as $user_attr_key => $field_detail) {
1265

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

    
1280
      // $value_instance not used, may have future use case
1281

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

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

    
1309
    // Allow other modules to have a say.
1310

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

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

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

    
1342

    
1343
} // end LdapUserConf class