Projet

Général

Profil

Paste
Télécharger (33,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / ldap / ldap_authentication / ldap_authentication.inc @ 91af538d

1
<?php
2

    
3
/**
4
 * @file
5
 * Ldap_authentication helper functions.
6
 */
7

    
8
/**
9
 * Helper function for ldap_authn_form_user_login_block_alter and ldap_authn_form_user_login_alter.
10
 *
11
 * @todo if form is being generated on non https and is set in preferences, set warning and end form development
12
 */
13
function _ldap_authentication_login_form_alter(&$form, &$form_state, $form_id) {
14

    
15
  if (!$auth_conf = ldap_authentication_get_valid_conf()) {
16
    return;
17
  }
18
  elseif (!$auth_conf->hasEnabledAuthenticationServers()) {
19
    return;
20
  }
21

    
22
  /**
23
   *
24
   * add validate function to test for ldap authentication
25
   * should be placed after user_login_authenticate_validate
26
   * 1. user_login_name_validate
27
   * 2. user_login_authenticate_validate
28
   * 3. external authentication validate functions
29
   * 4. user_login_final_validate
30
   *
31
   * as articulated above user_login_default_validators() in user.module
32
   *
33
   * without any other external authentication modules, this array will start out as:
34
   *    array('user_login_name_validate', 'user_login_authenticate_validate', 'user_login_final_validate')
35
   */
36

    
37
  if (@in_array('user_login_authenticate_validate', $form['#validate']) && $auth_conf->authenticationMode) {
38
    $key = array_search('user_login_authenticate_validate', $form['#validate']);
39
    $form['#validate'][$key] = 'ldap_authentication_core_override_user_login_authenticate_validate';
40
    array_splice($form['#validate'], $key + 1, 0, 'ldap_authentication_user_login_authenticate_validate');
41
  }
42

    
43
  if ($form_id == 'user_login_block') {
44
    $user_register = variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
45
    $vars = [
46
      'show_reset_pwd' => ldap_authentication_show_reset_pwd(),
47
      'auth_conf' => $auth_conf,
48
    ];
49

    
50
    $form['links']['#markup'] = theme('ldap_authentication_user_login_block_links', $vars);
51
  }
52

    
53
  // Add help information for entering in username/password.
54
  $auth_conf = ldap_authentication_get_valid_conf();
55
  if ($auth_conf) {
56
    if (isset($auth_conf->loginUIUsernameTxt)) {
57
      $form['name']['#description'] = t($auth_conf->loginUIUsernameTxt);
58
    }
59
    if (isset($auth_conf->loginUIPasswordTxt)) {
60
      $form['pass']['#description'] = t($auth_conf->loginUIPasswordTxt);
61
    }
62
    if ($auth_conf->templateUsageRedirectOnLogin) {
63
      $form['#submit'][] = 'ldap_authentication_check_for_email_template';
64
    }
65
  }
66
}
67

    
68
/**
69
 * Alter user editing form (profile form) based on ldap authentication configuration.
70
 *
71
 * @param array $form
72
 *   array from user profile.
73
 * @param array $form_state
74
 *   from user profile.
75
 *
76
 * @return NULL (alters $form by reference)
77
 */
78
function _ldap_authentication_form_user_profile_form_alter(&$form, $form_state) {
79
  // Keep in mind admin may be editing another users profile form.  don't assume current global $user.
80
  $auth_conf = ldap_authentication_get_valid_conf();
81
  if ($auth_conf && ldap_authentication_ldap_authenticated($form['#user'])) {
82
    if ($auth_conf->emailOption == LDAP_AUTHENTICATION_EMAIL_FIELD_REMOVE) {
83
      $form['account']['mail']['#access'] = FALSE;
84
    }
85
    elseif ($auth_conf->emailOption == LDAP_AUTHENTICATION_EMAIL_FIELD_DISABLE) {
86
      $form['account']['mail']['#disabled'] = TRUE;
87
      $form['account']['mail']['#description'] = t('This email address is automatically set and may not be changed.');
88
    }
89
    elseif ($auth_conf->emailOption == LDAP_AUTHENTICATION_EMAIL_FIELD_ALLOW) {
90
      // Email field is functional.
91
    }
92

    
93
    if (!ldap_authentication_show_reset_pwd($form['#user'])) {
94
      /**  If passwordOption = LDAP_AUTHENTICATION_PASSWORD_FIELD_HIDE then don't show the password fields,
95
       * otherwise show the fields but in a disabled state.
96
       */
97
      switch ($auth_conf->passwordOption) {
98

    
99
        case LDAP_AUTHENTICATION_PASSWORD_FIELD_HIDE:
100
          $form['account']['current_pass']['#access'] = FALSE;
101
          $form['account']['pass']['#access'] = FALSE;
102
          break;
103

    
104
        case LDAP_AUTHENTICATION_PASSWORD_FIELD_SHOW:
105
          // Show in a disabled state since ldap_authentication_show_reset_pwd() has returned FALSE.
106
          $form['account']['current_pass']['#disabled'] = TRUE;
107
          if ($auth_conf->ldapUserHelpLinkUrl) {
108
            $form['account']['current_pass']['#description'] = l(t($auth_conf->ldapUserHelpLinkText), $auth_conf->ldapUserHelpLinkUrl);
109
          }
110
          else {
111
            $form['account']['current_pass']['#description'] = t('The password cannot be changed using this website');
112
          }
113
          $form['account']['pass']['#disabled'] = TRUE;
114
          break;
115
      }
116
    }
117
  }
118
}
119

    
120
/**
121
 * Replaces the email address in $ldap_user with one from the template in
122
 * $auth_conf.
123
 *
124
 * @param array $ldap_user
125
 *   LDAP user entry.
126
 * @param LdapAuthenticationConf $auth_conf
127
 *   LDAP authentication configuration class.
128
 */
129
function _ldap_authentication_replace_user_email(&$ldap_user, $auth_conf, $tokens) {
130
  // Fallback template in case one was not specified.
131
  $template = '@username@localhost';
132
  if (!empty($auth_conf->emailTemplate)) {
133
    $template = $auth_conf->emailTemplate;
134
  }
135
  $ldap_user['mail'] = format_string($template, $tokens);
136
}
137

    
138
/**
139
 * User form validation will take care of username, pwd fields
140
 * this function validates ldap authentication specific.
141
 *
142
 * @param array $form_state
143
 *   array from user logon form.
144
 *
145
 * @return null, but success or failure is indicated by:
146
 *   -- form_set_error() to invalidate authentication process
147
 *   -- setting $form_state['uid'] to indicate successful authentication
148
 */
149
function _ldap_authentication_user_login_authenticate_validate(&$form_state, $return_user) {
150

    
151
  // Check if Flood control was triggered; if so, don't authenticate.
152
  if (isset($form_state['flood_control_triggered'])) {
153
    return;
154
  }
155

    
156
  $detailed_watchdog_log = variable_get('ldap_help_watchdog_detail', 0);
157

    
158
  // Default to name.
159
  $entered_name = $form_state['values']['name'];
160
  $authname_drupal_property = $form_field_name = 'name';
161

    
162
  // Email registration module populates name even though user entered email.
163
  if (!empty($form_state['values']['email'])) {
164
    $entered_name = $form_state['values']['email'];
165
    $authname_drupal_property = 'mail';
166
    $form_field_name = 'email';
167
  }
168

    
169
  // $authname is the name the user is authenticated with from the logon form // patch 1599632.
170
  $authname = $entered_name;
171

    
172
  if (empty($form_state['values']['pass']) || empty($form_state['values'][$form_field_name])) {
173
    return FALSE;
174
  }
175
  /*
176
   * If a fake form state was passed into this function from
177
   * _ldap_authentication_user_login_sso(), there will be a value outside of the
178
   * form_state[values] array to let us know that we are not authenticating with
179
   * a password, but instead just looking up a username/dn in LDAP since the web
180
   * server already authenticated the user.
181
   */
182
  $sso_login = (isset($form_state['sso_login']) && $form_state['sso_login']) ? TRUE : FALSE;
183

    
184
  // Patch 1599632.
185
  $watchdog_tokens = ['%username' => $authname, '%authname' => $authname];
186
  if ($detailed_watchdog_log) {
187
    watchdog('ldap_authentication', '%username : Beginning authentification....', $watchdog_tokens, WATCHDOG_DEBUG);
188
  }
189

    
190
  if (!$auth_conf = ldap_authentication_get_valid_conf()) {
191
    watchdog('ldap_authentication', 'Failed to get valid ldap authentication configuration.', [], WATCHDOG_ERROR);
192
    form_set_error('name', 'Server Error: Failed to get valid ldap authentication configuration.');
193
    return;
194
  }
195

    
196
  /**
197
  * I. Test for previous module authentication success.
198
  *
199
  * if already succeeded at authentication, $form_state['uid'] will be set by other authentication module.
200
  * - if LDAP Mixed mode is set, return and don't disrupt authentication process
201
  * - otherwise override other authenication by setting $form_state['uid'] = NULL
202
  */
203
  if (isset($form_state['uid']) && is_numeric($form_state['uid'])) {
204
    if ($auth_conf->authenticationMode == LDAP_AUTHENTICATION_MIXED || $form_state['uid'] == 1) {
205
      if ($detailed_watchdog_log) {
206
        watchdog('ldap_authentication', '%username : Previously authenticated in mixed mode or uid=1', $watchdog_tokens, WATCHDOG_DEBUG);
207
      }
208
      // Already passed a previous module's authentication validation.
209
      return;
210
    }
211
    elseif ($auth_conf->authenticationMode == LDAP_AUTHENTICATION_EXCLUSIVE) {
212
      if ($detailed_watchdog_log) {
213
        watchdog('ldap_authentication', '%username : Previously authenticated in exclusive mode or uid is not 1.  Clear uid
214
        in form_state and attempt ldap authentication.', $watchdog_tokens, WATCHDOG_DEBUG);
215
      }
216
      // Passed previous authentication, but only ldap should be used so override.
217
      $form_state['uid'] = NULL;
218
    }
219
  }
220

    
221
  /**
222
   * II. Exit if no authentication servers.
223
   */
224
  if (!$auth_conf->hasEnabledAuthenticationServers()) {
225
    watchdog('ldap_authentication', 'No LDAP servers configured.', [], WATCHDOG_ERROR);
226
    form_set_error('name', 'Server Error:  No LDAP servers configured.');
227
    return;
228
  }
229

    
230
  /**
231
   * III. determine if corresponding drupal account exists for $authname
232
   */
233
  $drupal_account_is_authmapped = FALSE;
234
  list($drupal_account, $drupal_account_is_authmapped) = ldap_authentication_corresponding_drupal_user($authname, $auth_conf, $watchdog_tokens);
235
  $drupal_account_exists = is_object($drupal_account);
236
  if ($drupal_account_exists && $drupal_account->uid == 1) {
237
    // User 1 is not allowed to ldap authenticate.
238
    return;
239
  }
240

    
241
  /**
242
   * IV. test credentials and if available get corresponding ldap user and ldap server
243
   */
244
  list($authentication_result, $ldap_user, $ldap_server_authenticated_on) = ldap_authentication_test_credentials($auth_conf, $sso_login, $authname, $form_state['values']['pass'], $watchdog_tokens);
245
  $params['account'] = $drupal_account;
246
  drupal_alter('ldap_entry', $ldap_user, $params);
247
  if ($authentication_result != LDAP_AUTHENTICATION_RESULT_SUCCESS) {
248
    ldap_authentication_fail_response($authentication_result, $auth_conf, $detailed_watchdog_log, $watchdog_tokens);
249
    return;
250
  }
251

    
252
  /**
253
   * IV.a Workaround for the provisioning server not always getting saved in
254
   *      the user object.  So we save it in a session variable as a backup.
255
   */
256
  if ($ldap_server_authenticated_on) {
257
    $_SESSION[LDAP_USER_SESSION_PROV_SID] = $ldap_server_authenticated_on->sid;
258
  }
259

    
260
  /**
261
   * V. if account_name_attr is set, drupal username is different than authname
262
   */
263
  if ($ldap_server_authenticated_on->account_name_attr != '') {
264
    $watchdog_tokens['%account_name_attr'] = $ldap_server_authenticated_on->account_name_attr;
265
    $drupal_accountname = $ldap_user['attr'][ldap_server_massage_text($ldap_server_authenticated_on->account_name_attr, 'attr_name', LDAP_SERVER_MASSAGE_QUERY_ARRAY)][0];
266
    if (!$drupal_accountname) {
267
      watchdog('ldap_authentication', 'Derived drupal username from attribute %account_name_attr returned no username for authname %authname.', $watchdog_tokens, WATCHDOG_ERROR);
268
      return;
269
    }
270
  }
271
  else {
272
    $drupal_accountname = $authname;
273
  }
274
  $watchdog_tokens['%drupal_accountname'] = $drupal_accountname;
275

    
276
  // @todo maybe we can add more tokens?
277
  $email_template_tokens = [
278
    '@username' => $drupal_accountname,
279
  ];
280

    
281
  $email_template_used = FALSE;
282

    
283
  /**
284
   * Ensures that we respect the email template handling settings.
285
   */
286
  if (!empty($auth_conf->emailTemplate)) {
287
    switch ($auth_conf->emailTemplateHandling) {
288
      case LDAP_AUTHENTICATION_EMAIL_TEMPLATE_IF_EMPTY:
289
        if (!empty($ldap_user['mail'])) {
290
          break;
291
        }
292
        // Deliberate fallthrough.
293
      case LDAP_AUTHENTICATION_EMAIL_TEMPLATE_ALWAYS:
294
        _ldap_authentication_replace_user_email($ldap_user, $auth_conf, $email_template_tokens);
295
        if ($detailed_watchdog_log) {
296
          watchdog('ldap_authentication', 'Using template generated email for %username', $watchdog_tokens, WATCHDOG_DEBUG);
297
        }
298
        $email_template_used = TRUE;
299
        break;
300
    }
301
  }
302

    
303
  /**
304
   * VI. Find or create corresponding drupal account and set authmaps
305
   *
306
   * at this point, the following are know:
307
   * - a corresponding ldap account has been found
308
   * - user's credentials tested against it and passed
309
   * - their drupal accountname has been derived
310
   *
311
   */
312

    
313
  /**
314
   * VI.A: Drupal account doesn't exist with $authname used to logon,
315
   *  but puid exists in another Drupal account; this means username has changed
316
   *  and needs to be saved in Drupal account
317
   *
318
   */
319
  if (!$drupal_account_exists && $ldap_server_authenticated_on) {
320
    $puid = $ldap_server_authenticated_on->userPuidFromLdapEntry($ldap_user['attr']);
321
    if ($puid) {
322
      $drupal_account = $ldap_server_authenticated_on->userUserEntityFromPuid($puid);
323
      if ($drupal_account) {
324
        $drupal_account_exists = TRUE;
325
        if ($drupal_accountname == $authname) {
326
          $user_edit = [$authname_drupal_property => $drupal_accountname];
327
        }
328
        else {
329
          $user_edit = ['name' => $drupal_accountname];
330
        }
331
        $drupal_account = user_save($drupal_account, $user_edit, 'ldap_user');
332
        user_set_authmaps($drupal_account, ["authname_ldap_user" => $authname]);
333
        $drupal_account_is_authmapped = TRUE;
334
      }
335
    }
336
  }
337

    
338
  /**
339
   * VI.B: existing Drupal account but not authmapped to ldap modules,
340
   * ldap authmap or disallow.
341
   */
342
  // Account already exists.
343
  if ($drupal_account_exists && !$drupal_account_is_authmapped) {
344
    if ($auth_conf->ldapUser->loginConflictResolve == LDAP_USER_CONFLICT_LOG) {
345
      if ($account_with_same_email = user_load_by_mail($ldap_user['mail'])) {
346
        $watchdog_tokens['%conflict_name'] = $account_with_same_email->name;
347
        watchdog('ldap_authentication', 'LDAP user with DN %dn has a naming conflict with a local drupal user %conflict_name', $watchdog_tokens, WATCHDOG_ERROR);
348
      }
349
      drupal_set_message(t('Another user already exists in the system with the same login name. You should contact the system administrator in order to solve this conflict.'), 'error');
350
      return;
351
    }
352
    // LDAP_authen.AC.disallow.ldap.drupal.
353
    else {
354
      // Add ldap_authentication authmap to user.  account name is fine here, though cn could be used.
355
      user_set_authmaps($drupal_account, ['authname_ldap_user' => $authname]);
356
      $drupal_account_is_authmapped = TRUE;
357
      if ($detailed_watchdog_log) {
358
        watchdog('ldap_authentication', 'set authmap for %username authname_ldap_user', $watchdog_tokens, WATCHDOG_DEBUG);
359
      }
360
    }
361
  }
362

    
363
  /**
364
   * VI.C: existing Drupal account with incorrect email.
365
   * Fix email if appropriate.
366
   */
367
  if ((!($auth_conf->templateUsageNeverUpdate && $email_template_used)) &&
368
      $drupal_account_exists &&
369
      $drupal_account->mail != $ldap_user['mail'] &&
370
      (
371
        $auth_conf->emailUpdate == LDAP_AUTHENTICATION_EMAIL_UPDATE_ON_LDAP_CHANGE_ENABLE_NOTIFY ||
372
        $auth_conf->emailUpdate == LDAP_AUTHENTICATION_EMAIL_UPDATE_ON_LDAP_CHANGE_ENABLE
373
      )) {
374
    $user_edit = ['mail' => $ldap_user['mail']];
375

    
376
    $watchdog_tokens['%username'] = $drupal_account->name;
377
    if (!$updated_account = user_save($drupal_account, $user_edit)) {
378
      watchdog('ldap_authentication', 'Failed to make changes to user %username updated %changed.', $watchdog_tokens, WATCHDOG_ERROR);
379
    }
380
    elseif ($auth_conf->emailUpdate == LDAP_AUTHENTICATION_EMAIL_UPDATE_ON_LDAP_CHANGE_ENABLE_NOTIFY) {
381
      if (isset($user_edit['mail'])) {
382
        $watchdog_tokens['%mail'] = $user_edit['mail'];
383
        drupal_set_message(t('Your e-mail has been updated to match your current account (%mail).', $watchdog_tokens), 'status');
384
      }
385
      if (isset($user_edit['name'])) {
386
        $watchdog_tokens['%new_username'] = $user_edit['name'];
387
        drupal_set_message(t('Your old account username %username has been updated to %new_username.', $watchdog_tokens), 'status');
388
      }
389
    }
390
  }
391

    
392
  /**
393
   * VI.C: no existing Drupal account.  consider provisioning Drupal account.
394
   */
395
  if (!$drupal_account_exists) {
396

    
397
    // VI.C.1 Do not provision Drupal account if another account has same email.
398
    if (($auth_conf->ldapUser->acctCreation == LDAP_USER_ACCOUNTS_WITH_SAME_EMAIL_DISABLED) && ($account_with_same_email = user_load_by_mail($ldap_user['mail']))) {
399
      $error = TRUE;
400
      /**
401
       * username does not exist but email does.
402
       * Since user_external_login_register does not deal with mail attribute
403
       * and the email conflict error needs to be caught beforehand, need to
404
       * throw error here.
405
       */
406
      if ($auth_conf->templateUsageResolveConflict && (!$email_template_used)) {
407
        if ($detailed_watchdog_log) {
408
          watchdog('ldap_authentication', 'Conflict detected, using template generated email for %username', $watchdog_tokens, WATCHDOG_DEBUG);
409
        }
410
        _ldap_authentication_replace_user_email($ldap_user, $auth_conf, $email_template_tokens);
411
        $email_template_used = TRUE;
412
        // Recheck with the template email to make sure it doesn't also exist.
413
        if ($account_with_same_email = user_load_by_mail($ldap_user['mail'])) {
414
          $error = TRUE;
415
        }
416
      }
417
      if ($error) {
418
        $watchdog_tokens['%duplicate_name'] = $account_with_same_email->name;
419
        watchdog('ldap_authentication', 'LDAP user with DN %dn has email address
420
          (%mail) conflict with a drupal user %duplicate_name', $watchdog_tokens, WATCHDOG_ERROR);
421
        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');
422
        return;
423
      }
424
    }
425

    
426
    // VI.C.2 Do not provision Drupal account if provisioning disabled.
427
    if (!$auth_conf->ldapUser->provisionEnabled(LDAP_USER_PROV_DIRECTION_TO_DRUPAL_USER, LDAP_USER_DRUPAL_USER_PROV_ON_AUTHENTICATE)) {
428
      watchdog('ldap_user', 'Drupal account for authname=%authname account name=%account_name_attr does not exist and provisioning of Drupal accounts on authentication is not enabled', $watchdog_tokens, WATCHDOG_INFO);
429
      return;
430
    }
431

    
432
    /**
433
     * VI.C.3 Provision Drupal account.
434
     * New ldap_authentication provisioned account could
435
     * letuser_external_login_register create the account and set authmaps, but
436
     * would need to add mail and any other user->data data in
437
     * hook_user_presave which would mean requerying ldap or having a global
438
     * variable.  At this point the account does not exist, so there is no
439
     * reason not to create it here.
440
     *
441
     * @todo create patch for core user module's user_external_login_register
442
     * to deal with new external accounts a little tweak to add user->data and
443
     * mail etc as parameters would make it more useful for external
444
     * authentication modules.
445
     */
446
    $user_register = variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
447
    if ($auth_conf->ldapUser->acctCreation == LDAP_USER_ACCT_CREATION_USER_SETTINGS_FOR_LDAP &&
448
            $user_register == USER_REGISTER_ADMINISTRATORS_ONLY) {
449
      watchdog('ldap_user', 'Failed to create account for %drupal_accountname. Administrative user must create user.',
450
          $watchdog_tokens, WATCHDOG_ERROR);
451
      form_set_error('name', t('Server Error: Attempt to create account for %drupal_accountname failed. Administrative user must create user.',
452
          $watchdog_tokens));
453
      return;
454
    }
455

    
456
    if ($auth_conf->ldapUser->acctCreation == LDAP_AUTHENTICATION_ACCT_CREATION_USER_SETTINGS_FOR_LDAP &&
457
        variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) {
458
      // If admin approval required, set status to 0.
459
      $user_edit = ['name' => $drupal_accountname, 'status' => 0];
460
    }
461
    else {
462
      $user_edit = ['name' => $drupal_accountname, 'status' => 1];
463
    }
464

    
465
    // If the email template was used, we want to pass in the email that was
466
    // generated so that its not overridden by the provisioner.
467
    if ($email_template_used) {
468
      $user_edit['mail'] = $ldap_user['mail'];
469
    }
470

    
471
    // don't pass in ldap user to provisionDrupalAccount, because want to requery with correct attributes needed
472
    // this may be a case where efficiency dictates querying for all attributes.
473
    $drupal_account = $auth_conf->ldapUser->provisionDrupalAccount(NULL, $user_edit, $ldap_user, TRUE);
474

    
475
    if ($drupal_account === FALSE) {
476
      watchdog('ldap_user', 'Failed to find or create %drupal_accountname on logon.', $watchdog_tokens, WATCHDOG_ERROR);
477
      form_set_error('name', t('Server Error: Failed to create Drupal user account for %drupal_accountname', $watchdog_tokens));
478
      return;
479
    }
480
    else {
481
      user_set_authmaps($drupal_account, ['authname_ldap_user' => $authname]);
482
      // Using Rules allows emails to be fired and many other possible reactions
483
      // to the creation of a user.
484
      if (module_exists('rules')) {
485
        rules_invoke_event('ldap_user_created', $drupal_account, $email_template_used);
486
      }
487
    }
488
  }
489

    
490
  /**
491
  * we now have valid, ldap authenticated username with an account authmapped to ldap_authentication.
492
  * since user_external_login_register can't deal with user mail attribute and doesn't do much else, it is not
493
  * being used here.
494
  *
495
  * without doing the user_login_submit,
496
  * [#1009990],[#1865938]
497
  */
498

    
499
  $form_state['uid'] = $drupal_account->uid;
500
  return ($return_user) ? $drupal_account : NULL;
501
}
502

    
503
/**
504
 * Given authname, determine if corresponding drupal account exists and is authmapped.
505
 */
506
function ldap_authentication_corresponding_drupal_user($authname, $auth_conf, &$watchdog_tokens) {
507
  $detailed_watchdog_log = variable_get('ldap_help_watchdog_detail', 0);
508
  if (!($drupal_account = user_load_by_name($authname)) && !($drupal_account = user_load_by_mail($authname))) {
509
    $uid = db_query("SELECT uid FROM {authmap} WHERE authname = :authname AND module = 'ldap_user'", [':authname' => $authname])->fetchColumn();
510
    $drupal_account = $uid ? user_load($uid) : FALSE;
511
  }
512

    
513
  if (is_object($drupal_account)) {
514
    // Patch 1599632.
515
    $authmaps = user_get_authmaps($authname);
516
    $drupal_account_is_authmapped = isset($authmaps['ldap_user']);
517
    $user_data = $drupal_account->data;
518
    if ($drupal_account->uid == 1 && $detailed_watchdog_log) {
519
      watchdog('ldap_authentication', '%username : Drupal username maps to user 1, so do not authenticate with ldap', $watchdog_tokens, WATCHDOG_DEBUG);
520
    }
521
    elseif ($detailed_watchdog_log) {
522
      watchdog('ldap_authentication', '%username : Drupal User Account found.  Continuing on to attempt ldap authentication', $watchdog_tokens, WATCHDOG_DEBUG);
523
    }
524
  }
525
  // Account does not exist.
526
  else {
527
    $drupal_account_is_authmapped = FALSE;
528
    if ($auth_conf->ldapUser->createLDAPAccounts == FALSE) {
529
      if ($detailed_watchdog_log) {
530
        watchdog('ldap_authentication', '%username : Drupal User Account not found and configuration is set to not create new accounts.', $watchdog_tokens, WATCHDOG_DEBUG);
531
      }
532
    }
533
    if ($detailed_watchdog_log) {
534
      watchdog('ldap_authentication', '%username : Existing Drupal User Account not found.  Continuing on to attempt ldap authentication', $watchdog_tokens, WATCHDOG_DEBUG);
535
    }
536
  }
537
  return [$drupal_account, $drupal_account_is_authmapped];
538
}
539

    
540
/**
541
 *
542
 */
543
function ldap_authentication_test_credentials($auth_conf, $sso_login, $authname, $password, &$watchdog_tokens) {
544
  $detailed_watchdog_log = variable_get('ldap_help_watchdog_detail', 0);
545
  $authentication_result = LDAP_AUTHENTICATION_RESULT_FAIL_GENERIC;
546
  $ldap_user = FALSE;
547
  $ldap_server = NULL;
548
  foreach ($auth_conf->enabledAuthenticationServers as $sid => $ldap_server) {
549
    $watchdog_tokens['%sid'] = $sid;
550
    $watchdog_tokens['%bind_method'] = $ldap_server->bind_method;
551
    if ($detailed_watchdog_log) {
552
      watchdog('ldap_authentication', '%username : Trying server %sid where bind_method = %bind_method', $watchdog_tokens, WATCHDOG_DEBUG);
553
    }
554

    
555
    // #1 CONNECT TO SERVER.
556
    $authentication_result = LDAP_AUTHENTICATION_RESULT_FAIL_GENERIC;
557
    $result = $ldap_server->connect();
558
    if ($result != LDAP_SUCCESS) {
559
      $authentication_result = LDAP_AUTHENTICATION_RESULT_FAIL_CONNECT;
560
      $watchdog_tokens['%err_msg'] = $ldap_server->errorMsg('ldap');
561
      if ($detailed_watchdog_log) {
562
        watchdog('ldap_authentication', '%username : Failed connecting to %sid.  Error: %err_msg', $watchdog_tokens, WATCHDOG_DEBUG);
563
      }
564
      $watchdog_tokens['%err_msg'] = NULL;
565
      // Next server, please.
566
      continue;
567
    }
568
    elseif ($detailed_watchdog_log) {
569
      watchdog('ldap_authentication', '%username : Success at connecting to %sid', $watchdog_tokens, WATCHDOG_DEBUG);
570
    }
571

    
572
    $bind_success = FALSE;
573
    if ($ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_SERVICE_ACCT) {
574
      $bind_success = ($ldap_server->bind(NULL, NULL, FALSE) == LDAP_SUCCESS);
575
    }
576
    elseif ($ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_ANON ||
577
        $ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_ANON_USER) {
578
      $bind_success = ($ldap_server->bind(NULL, NULL, TRUE) == LDAP_SUCCESS);
579
    }
580
    elseif ($sso_login) {
581
      watchdog('ldap_authentication', 'Trying to use SSO with LDAP_SERVERS_BIND_METHOD_USER bind method.', $watchdog_tokens, WATCHDOG_ERROR);
582
    }
583
    elseif ($ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_USER && $sso_login == FALSE) {
584
      // With sso enabled this method of binding isn't valid.
585
      foreach ($ldap_server->basedn as $basedn) {
586
        $search = ['%basedn', '%username'];
587
        $transformname = $ldap_server->userUsernameToLdapNameTransform($authname, $watchdog_tokens);
588
        $replace = [$basedn, $transformname];
589
        $userdn = str_replace($search, $replace, $ldap_server->user_dn_expression);
590
        $bind_success = ($ldap_server->bind($userdn, $password, FALSE) == LDAP_SUCCESS);
591
        if ($bind_success) {
592
          break;
593
        }
594
      }
595
    }
596
    else {
597
      watchdog('ldap_authentication', 'No bind method set in ldap_server->bind_method in _ldap_authentication_user_login_authenticate_validate.', $watchdog_tokens, WATCHDOG_ERROR);
598
    }
599

    
600
    if (!$bind_success) {
601
      if ($detailed_watchdog_log) {
602
        $watchdog_tokens['%err_text'] = $ldap_server->errorMsg('ldap');
603
        watchdog('ldap_authentication', '%username : Trying server %sid where bind_method = %bind_method.  Error: %err_text', $watchdog_tokens, WATCHDOG_DEBUG);
604
        $watchdog_tokens['%err_text'] = NULL;
605
      }
606
      $authentication_result = ($ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_USER) ? LDAP_AUTHENTICATION_RESULT_FAIL_CREDENTIALS : LDAP_AUTHENTICATION_RESULT_FAIL_BIND;
607
      // If bind fails, onto next server.
608
      continue;
609
    }
610

    
611
    // #3 DOES USER EXIST IN SERVER'S LDAP.
612
    if ($ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_ANON_USER) {
613
      $ldap_user = $ldap_server->userUserNameToExistingLdapEntry($authname);
614
    }
615
    elseif ($sso_login) {
616
      $ldap_user = $ldap_server->userUserNameToExistingLdapEntry($authname);
617
      if ($detailed_watchdog_log) {
618
        $watchdog_tokens['%result'] = var_export($result, TRUE);
619
        watchdog('ldap_authentication', '%username : attempting single sign-on
620
          login in bind_method of LDAP_SERVERS_BIND_METHOD_USER. Result of
621
          userUserNameToExistingLdapEntry: <pre>%result</pre>', $watchdog_tokens, WATCHDOG_DEBUG);
622
      }
623
    }
624
    else {
625
      $ldap_user = $ldap_server->userUserNameToExistingLdapEntry($authname);
626
    }
627

    
628
    if (!$ldap_user) {
629
      if ($detailed_watchdog_log) {
630
        $watchdog_tokens['%err_text'] = $ldap_server->errorMsg('ldap');
631
        watchdog('ldap_authentication', '%username : Trying server %sid where bind_method = %bind_method.  Error: %err_text', $watchdog_tokens, WATCHDOG_DEBUG);
632
        $watchdog_tokens['%err_text'] = NULL;
633
      }
634
      if ($ldap_server->ldapErrorNumber()) {
635
        $authentication_result = LDAP_AUTHENTICATION_RESULT_FAIL_SERVER;
636
        break;
637
      }
638
      $authentication_result = LDAP_AUTHENTICATION_RESULT_FAIL_FIND;
639
      // Next server, please.
640
      continue;
641
    }
642

    
643
    $watchdog_tokens['%dn'] = $ldap_user['dn'];
644
    $watchdog_tokens['%mail'] = $ldap_user['mail'];
645

    
646
    /**
647
     * #4 CHECK ALLOWED AND EXCLUDED LIST AND PHP FOR ALLOWED USERS
648
     */
649
    if (!$auth_conf->allowUser($authname, $ldap_user)) {
650
      $authentication_result = LDAP_AUTHENTICATION_RESULT_FAIL_DISALLOWED;
651
      // Regardless of how many servers, disallowed user fails.
652
      break;
653
    }
654

    
655
    /**
656
     * #5 TEST PASSWORD
657
     */
658
    $credentials_pass = FALSE;
659
    if ($sso_login) {
660
      /** If we have $sso_login passed in as true from the fake form state in
661
        * passed from _ldap_authentication_user_login_sso(), we will be relying
662
        * on the webserver for actually authenticating the user, either by NTLM
663
        * or user/password if configured as a fallback. Since the webserver has
664
        * already authenticated the user, and the web server only contains the
665
        * user's LDAP user name, instead of binding on the username/pass, we
666
        * simply look up the user's account in LDAP, and make sure it matches
667
        * what is contained in the global $_SERVER array populated by the web
668
        * server authentication.
669
        */
670
      $credentials_pass = (boolean) ($ldap_user);
671
    }
672
    elseif ($ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_USER) {
673
      /**
674
        * With user bind method, the only way we can reach this part of the
675
        * code is when the pw has already been checked and $ldap_user could be
676
        * loaded, so we're good to go.
677
        */
678
      $credentials_pass = TRUE;
679
    }
680
    else {
681
      $credentials_pass = ($ldap_server->bind($ldap_user['dn'], $password, FALSE) == LDAP_SUCCESS);
682
    }
683
    if (!$credentials_pass) {
684
      if ($detailed_watchdog_log) {
685
        $watchdog_tokens['%err_text'] = $ldap_server->errorMsg('ldap');
686
        watchdog('ldap_authentication', '%username : Testing user credentials on server %sid where bind_method = %bind_method.  Error: %err_text', $watchdog_tokens, WATCHDOG_DEBUG);
687
        $watchdog_tokens['%err_text'] = NULL;
688
      }
689
      $authentication_result = LDAP_AUTHENTICATION_RESULT_FAIL_CREDENTIALS;
690
      // Next server, please.
691
      continue;
692
    }
693
    else {
694
      $authentication_result = LDAP_AUTHENTICATION_RESULT_SUCCESS;
695
      if ($ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_ANON_USER) {
696
        // After successful bind, lookup user again to get private attributes.
697
        $ldap_user = $ldap_server->userUserNameToExistingLdapEntry($authname);
698
        $watchdog_tokens['%mail'] = $ldap_user['mail'];
699
      }
700
      if ($ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_SERVICE_ACCT ||
701
          $ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_ANON_USER) {
702
        $ldap_server->disconnect();
703
      }
704
      // Update ldapUser with the sid of the server that the user authenticated
705
      // on if that option was enabled in the LDAP user configuration.
706
      if ($auth_conf->ldapUser->drupalAcctProvisionServer == LDAP_USER_AUTH_SERVER_SID) {
707
        $auth_conf->ldapUser->drupalAcctProvisionServer = $ldap_server->sid;
708
      }
709
      // Success.
710
      break;
711
    }
712
  }
713
  // End loop through servers.
714
  $watchdog_tokens['%result'] = $result;
715
  $watchdog_tokens['%auth_result'] = $authentication_result;
716
  $watchdog_tokens['%err_text'] = _ldap_authentication_err_text($authentication_result);
717
  if ($detailed_watchdog_log) {
718
    watchdog('ldap_authentication', '%username : Authentication result id=%result auth_result=%auth_result (%err_text)', $watchdog_tokens, WATCHDOG_DEBUG);
719
  }
720

    
721
  return [$authentication_result, $ldap_user, $ldap_server];
722
}
723

    
724
/**
725
 *
726
 */
727
function ldap_authentication_fail_response($authentication_result, $auth_conf, $detailed_watchdog_log, &$watchdog_tokens) {
728
  $watchdog_tokens['%err_text'] = _ldap_authentication_err_text($authentication_result);
729
  // Fail scenario 1.  ldap auth exclusive and failed  throw error so no other authentication methods are allowed.
730
  if ($auth_conf->authenticationMode == LDAP_AUTHENTICATION_EXCLUSIVE) {
731
    if ($detailed_watchdog_log) {
732
      watchdog('ldap_authentication', '%username : setting error because failed at ldap and
733
        LDAP_AUTHENTICATION_EXCLUSIVE is set to true.  So need to stop authentication of Drupal user that is not user 1.
734
        error message: %err_text', $watchdog_tokens, WATCHDOG_DEBUG);
735
    }
736
    form_set_error('name', $watchdog_tokens['%err_text']);
737
  }
738
  else {
739
    // Fail scenario 2.  simply fails ldap.  return false, but don't throw form error
740
    // don't show user message, may be using other authentication after this that may succeed.
741
    if ($detailed_watchdog_log) {
742
      watchdog('ldap_authentication',
743
        '%username : Failed ldap authentication.
744
        User may have authenticated successfully by other means in a mixed authentication site.
745
        LDAP Authentication Error #: %auth_result  error message: %err_text',
746
        $watchdog_tokens,
747
        WATCHDOG_DEBUG
748
        );
749
    }
750
  }
751
}
752

    
753
/**
754
 * Get human readable authentication error string.
755
 *
756
 * @param int $error
757
 *   as LDAP_AUTHENTICATION_RESULT_* constant defined in
758
 *   ldap_authentication.module.
759
 *
760
 * @return string human readable error text
761
 */
762
function _ldap_authentication_err_text($error) {
763

    
764
  $msg = t('unknown error: ' . $error);
765
  switch ($error) {
766
    case LDAP_AUTHENTICATION_RESULT_FAIL_CONNECT:
767
      $msg = t('Failed to connect to ldap server');
768
      break;
769

    
770
    case LDAP_AUTHENTICATION_RESULT_FAIL_BIND:
771
      $msg = t('Failed to bind to ldap server');
772
      break;
773

    
774
    case LDAP_AUTHENTICATION_RESULT_FAIL_FIND:
775
      $msg = t('Sorry, unrecognized username or password.');
776
      break;
777

    
778
    case LDAP_AUTHENTICATION_RESULT_FAIL_DISALLOWED:
779
      $msg = t('User disallowed');
780
      break;
781

    
782
    case LDAP_AUTHENTICATION_RESULT_FAIL_CREDENTIALS:
783
      $msg = t('Sorry, unrecognized username or password.');
784
      break;
785

    
786
    case LDAP_AUTHENTICATION_RESULT_FAIL_GENERIC:
787
      $msg = t('Sorry, unrecognized username or password.');
788
      break;
789

    
790
    case LDAP_AUTHENTICATION_RESULT_FAIL_SERVER:
791
      $msg = t('Authentication Server or Configuration Error.');
792
      break;
793

    
794
  }
795

    
796
  return $msg;
797
}