Projet

Général

Profil

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

root / drupal7 / sites / all / modules / ldap / ldap_sso / ldap_sso.module @ 5136ce55

1
<?php
2

    
3
/**
4
 * @file
5
 * This module injects itself into Drupal's Authentication stack.
6
 */
7

    
8
/**
9
 * Implements hook_menu().
10
 */
11
function ldap_sso_menu() {
12
  $items = array();
13

    
14
  $items['user/login/sso'] = array(
15
    'title' => 'Log In',
16
    'page callback' => 'ldap_sso_user_login_sso',
17
    'access callback' => '_ldap_authentication_user_access',
18
    'type' => MENU_NORMAL_ITEM,
19
  );
20

    
21
  return $items;
22
}
23

    
24

    
25
/**
26
 * Implements hook_user_logout().
27
 *
28
 * The user just logged out.
29
 */
30
function ldap_sso_user_logout($account) {
31
  $auth_conf = ldap_authentication_get_valid_conf();
32
  if ($auth_conf->seamlessLogin == 1) {
33
    $cookie_string = 'do not auto login';
34
    $cookie_timeout = (int) $auth_conf->cookieExpire;
35
    setcookie('seamless_login', $cookie_string, (($cookie_timeout == -1) ? 0 : $cookie_timeout + time()), base_path(), "");
36
    ldap_servers_set_globals('_SESSION', 'seamless_login', $cookie_string);
37
  }
38
}
39

    
40
/**
41
 * Implements hook_boot().
42
 *
43
 * Perform setup tasks. This entry point is used because hook_user_load no
44
 * longer runs on anonymous users, and hook_boot is guaranteed to run,
45
 * regardless of cache.
46
 */
47
function ldap_sso_boot() {
48

    
49
  if (!drupal_is_cli() && ($GLOBALS['user']->uid == 0)) {
50

    
51
    if (ldap_sso_path_excluded_from_sso()) {
52
      return;
53
    }
54
    module_load_include('module', 'ldap_servers');
55

    
56
    if (!(isset($_COOKIE['seamless_login'])) || $_COOKIE['seamless_login'] == 'auto login') {
57
      if ((arg(0) == 'user' && !(is_numeric(arg(1)))) || arg(0) == 'logout') {
58
        return;
59
      }
60
      else {
61
        if (isset($_COOKIE['seamless_login_attempted'])) {
62
          $login_attempted = $_COOKIE['seamless_login_attempted'];
63
        }
64
        else {
65
          $login_attempted = FALSE;
66
        }
67

    
68
        require_once DRUPAL_ROOT . '/includes/common.inc';
69
        require_once DRUPAL_ROOT . '/' . variable_get('path_inc', 'includes/path.inc');
70
        $ldap_authentication_conf = variable_get('ldap_authentication_conf', array());
71

    
72
        if (isset($ldap_authentication_conf['seamlessLogin']) && $ldap_authentication_conf['seamlessLogin'] == 1 && ($login_attempted != 'true')) {
73
          if ($ldap_authentication_conf['cookieExpire'] == 0) {
74
            setcookie("seamless_login_attempted", 'true', 0, base_path(), "");
75
          }
76
          else {
77
            setcookie('seamless_login_attempted', 'true', time() + (int) $ldap_authentication_conf['cookieExpire'], base_path(), "");
78
          }
79
          ldap_servers_set_globals('_SESSION', 'seamless_login_attempted', $login_attempted);
80

    
81
          drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
82
          // Seems redundant, but need to check this again after additional
83
          // bootstrap.
84
          if (ldap_sso_path_excluded_from_sso()) {
85
            return;
86
          }
87
          // Add the query key to the drupal_goto() options array only if there
88
          // is a destination set. This prevents infinite redirect loops.
89
          $options = array();
90
          $destination = drupal_get_destination();
91
          if (!empty($destination['destination'])) {
92
            $options['query'] = $destination;
93
          }
94
          drupal_goto('user/login/sso', $options);
95
        }
96
        else {
97
          return;
98
        }
99
      }
100
    }
101
  }
102
}
103

    
104
/**
105
 * Default excluded paths.
106
 */
107
function ldap_sso_default_excluded_paths() {
108
  return array(
109
    'admin/config/search/clean-urls/check',
110
  );
111
}
112

    
113
/**
114
 * Paths excluded from SSO.
115
 */
116
function ldap_sso_path_excluded_from_sso($path = FALSE) {
117
  module_load_include('module', 'ldap_servers');
118
  $result = FALSE;
119
  if ($path) {
120
    // Don't derive.
121
  }
122
  elseif (ldap_servers_get_globals('_SERVER', 'PHP_SELF') == '/index.php') {
123
    $path = $_GET['q'];
124
  }
125
  else {
126
    // Cron.php, etc.
127
    $path = ltrim(ldap_servers_get_globals('_SERVER', 'PHP_SELF'), '/');
128
  }
129

    
130
  if (in_array($path, ldap_sso_default_excluded_paths())) {
131
    return TRUE;
132
  }
133

    
134
  $ldap_authentication_conf = variable_get('ldap_authentication_conf', array());
135

    
136
  if (isset($ldap_authentication_conf['ssoExcludedHosts']) && is_array($ldap_authentication_conf['ssoExcludedHosts'])) {
137
    $host = ldap_servers_get_globals('_SERVER', 'SERVER_NAME');
138
    foreach ($ldap_authentication_conf['ssoExcludedHosts'] as $host_to_check) {
139
      if ($host_to_check == $host) {
140
        return TRUE;
141
      }
142
    }
143
  }
144

    
145
  if (isset($ldap_authentication_conf['ssoExcludedPaths'])) {
146
    $patterns = implode("\r\n", $ldap_authentication_conf['ssoExcludedPaths']);
147
    if ($patterns) {
148
      if (function_exists('drupal_get_path_alias')) {
149
        $path = drupal_get_path_alias($path);
150
      }
151
      $path = (function_exists('drupal_strtolower')) ? drupal_strtolower($path) : strtolower($path);
152

    
153
      $to_replace = array(
154
        // Newlines.
155
        '/(\r\n?|\n)/',
156
        // Asterisks.
157
        '/\\\\\*/',
158
        // <front>.
159
        '/(^|\|)\\\\<front\\\\>($|\|)/',
160
      );
161
      $replacements = array(
162
        '|',
163
        '.*',
164
        '\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\2',
165
      );
166
      $patterns_quoted = preg_quote($patterns, '/');
167
      $regex = '/^(' . preg_replace($to_replace, $replacements, $patterns_quoted) . ')$/';
168
      $result = (bool) preg_match($regex, $path);
169
    }
170
  }
171

    
172
  return $result;
173

    
174
}
175

    
176

    
177
/**
178
 * A proxy function for the actual authentication routine.
179
 *
180
 * This is in place so various implementations of grabbing NTLM credentials can
181
 * be used and selected from an administration page. This is the real gatekeeper
182
 * since this assumes that any NTLM authentication from the underlying web
183
 * server is good enough, and only checks that there are values in place for the
184
 * user name, and anything else that is set for a particular implementation. In
185
 * the case that there are no credentials set by the underlying web server, the
186
 * user is redirected to the normal user login form.
187
 */
188
function ldap_sso_user_login_sso() {
189

    
190
  $detailed_watchdog_log = variable_get('ldap_help_watchdog_detail', 0);
191
  $auth_conf = ldap_authentication_get_valid_conf();
192

    
193
  if ($detailed_watchdog_log) {
194
    $watchdog_tokens = array(
195
      '!implementation' => $auth_conf->ldapImplementation,
196
      '!enabled' => $auth_conf->ssoEnabled,
197
      '!server_remote_user' => @$_SERVER['REMOTE_USER'],
198
      '!server_redirect_remote_user' => @$_SERVER['REDIRECT_REMOTE_USER'],
199
      '!ssoRemoteUserStripDomainName' => $auth_conf->ssoRemoteUserStripDomainName,
200
      '!seamlessLogin' => $auth_conf->seamlessLogin,
201
    );
202

    
203
    watchdog(
204
      'ldap_sso',
205
      'ldap_sso_user_login_sso.step1: implementation: !implementation, enabled: !enabled, server_remote_user: !server_remote_user, server_redirect_remote_user: !server_redirect_remote_user, ssoRemoteUserStripDomainName: !ssoRemoteUserStripDomainName,seamlessLogin: !seamlessLogin',
206
      $watchdog_tokens,
207
      WATCHDOG_DEBUG
208
    );
209
  }
210

    
211
  // Step 1.  Derive $remote_user, $realm, and $domain from $_SERVER variable.
212
  $remote_user = NULL;
213
  $realm = NULL;
214
  $domain = NULL;
215

    
216
  switch ($auth_conf->ldapImplementation) {
217
    case 'mod_auth_sspi':
218
      $remote_user = FALSE;
219
      if ($remote_user = ldap_servers_get_globals('_SERVER', 'REMOTE_USER')) {
220
      }
221
      else {
222
        $remote_user = ldap_servers_get_globals('_SERVER', 'REDIRECT_REMOTE_USER');
223
      }
224
      break;
225

    
226
    case 'mod_auth_kerb':
227
      if ($remote_user = ldap_servers_get_globals('_SERVER', 'REMOTE_USER')) {
228
      }
229
      else {
230
        $remote_user = ldap_servers_get_globals('_SERVER', 'REDIRECT_REMOTE_USER');
231
      }
232

    
233
      if ($remote_user && preg_match('/^([A-Za-z0-9_\-\.]+)@([A-Za-z0-9_\-.]+)$/', $remote_user, $matches)) {
234
        $remote_user = $matches[1];
235
        // This can be used later if realms is ever supported properly.
236
        $realm = $matches[2];
237
      }
238
      break;
239
  }
240

    
241
  if ($detailed_watchdog_log) {
242
    $watchdog_tokens['!remote_user'] = $remote_user;
243
    $watchdog_tokens['!realm'] = $realm;
244
    watchdog('ldap_authentication', 'ldap_sso_user_login_sso.implementation: username=!remote_user, (realm=!realm) found',
245
              $watchdog_tokens, WATCHDOG_DEBUG);
246
  }
247

    
248
  if ($remote_user) {
249
    if ($auth_conf->ssoRemoteUserStripDomainName) {
250
      // Might be in form <remote_user>@<domain> or <domain>\<remote_user>.
251
      $domain = NULL;
252
      $exploded = preg_split('/[\@\\\\]/', $remote_user);
253
      if (count($exploded) == 2) {
254
        if (strpos($remote_user, '@') !== FALSE) {
255
          $remote_user = $exploded[0];
256
          $domain = $exploded[1];
257
        }
258
        else {
259
          $domain = $exploded[0];
260
          $remote_user = $exploded[1];
261
        }
262
        if ($detailed_watchdog_log) {
263
          $watchdog_tokens['!remote_user'] = $remote_user;
264
          $watchdog_tokens['!domain'] = $domain;
265
          watchdog('ldap_authentication', 'ldap_sso_user_login_sso.stripdomain: remote_user=!remote_user, domain=!domain', $watchdog_tokens, WATCHDOG_DEBUG);
266
        }
267
      }
268
    }
269

    
270
    if ($detailed_watchdog_log) {
271
      $watchdog_tokens['!remote_user'] = $remote_user;
272
      $watchdog_tokens['!realm'] = $realm;
273
      $watchdog_tokens['!domain'] = $domain;
274
      watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user: username=!remote_user, (realm=!realm, domain=!domain) found', $watchdog_tokens, WATCHDOG_DEBUG);
275
    }
276
    $fake_form_state = array(
277
      'values' => array(
278
        'name' => check_plain($remote_user),
279
        'pass' => user_password(20),
280
      ),
281
      'sso_login' => TRUE,
282
    );
283

    
284
    // Make sure we're populating the global user object so that we can log this
285
    // user in.
286
    global $user;
287
    $user = ldap_authentication_user_login_authenticate_validate(array(), $fake_form_state, TRUE);
288

    
289

    
290
    if ($detailed_watchdog_log) {
291
      $watchdog_tokens['!uid'] = is_object($user) ? $user->uid : NULL;
292
      watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user: uid of user=!uid', $watchdog_tokens, WATCHDOG_DEBUG);
293
    }
294

    
295
    if ($user && $user->uid > 0) {
296
      // Reload the account to ensure we have a fully populated user object.
297
      $user = user_load($user->uid);
298

    
299
      if ($auth_conf->seamlessLogin == 1) {
300
        if ($detailed_watchdog_log) {
301
          watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user.user_success.seamlessLogin', $watchdog_tokens, WATCHDOG_DEBUG);
302
        }
303
        setcookie("seamless_login", 'auto login', time() + $auth_conf->cookieExpire, base_path(), "");
304
        ldap_servers_set_globals('_SESSION', 'seamless_login', 'auto login');
305
        setcookie("seamless_login_attempted", '');
306
        ldap_servers_delete_globals('_SESSION', 'seamless_login_attempted');
307
        // Make sure we tell Drupal to create the session cookie for this
308
        // authenticated user.
309
      }
310
      user_login_finalize();
311
      if ($auth_conf->ssoNotifyAuthentication) {
312
        drupal_set_message(theme('ldap_authentication_login_message',
313
          array('message' => t('You have been successfully authenticated'))));
314
      }
315
      if ($detailed_watchdog_log) {
316
        watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user.user_success.drupal_goto front', $watchdog_tokens, WATCHDOG_DEBUG);
317
      }
318
      drupal_goto('<front>');
319
    }
320
    else {
321
      if ($auth_conf->seamlessLogin == 1) {
322
        if ($detailed_watchdog_log) {
323
          watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user.user_fail.seamlessLogin', $watchdog_tokens, WATCHDOG_DEBUG);
324
        }
325
        setcookie("seamless_login", 'do not auto login', time() + $auth_conf->cookieExpire, base_path(), "");
326
        ldap_servers_set_globals('_SESSION', 'seamless_login', 'do not auto login');
327
      }
328
      drupal_set_message(theme('ldap_authentication_message_not_found', array(
329
        'message' => t('Sorry, your LDAP credentials were not found, or the LDAP server is not available. You may log in with other credentials on the !user_login_form.',
330
          array('!user_login_form' => l(t('user login form'), 'user/login'))))
331
        ), 'error');
332
      if ($detailed_watchdog_log) {
333
        watchdog('ldap_authentication', 'ldap_sso_user_login_sso.remote_user.user_fail.drupal_goto user/logint', $watchdog_tokens, WATCHDOG_DEBUG);
334
      }
335
      drupal_goto('user/login');
336
    }
337
  }
338
  else {
339
    if ($detailed_watchdog_log) {
340
      watchdog('ldap_authentication', '$_SERVER[\'REMOTE_USER\'] not found', array(), WATCHDOG_DEBUG);
341
    }
342
    if ($auth_conf->seamlessLogin == 1) {
343
      setcookie("seamless_login", 'do not auto login', time() + $auth_conf->cookieExpire, base_path(), "");
344
      ldap_servers_set_globals('_SESSION', 'seamless_login', 'do not auto login');
345
      if ($detailed_watchdog_log) {
346
        watchdog('ldap_authentication', 'ldap_sso_user_login_sso.no_remote_user.seamlessLogin', $watchdog_tokens, WATCHDOG_DEBUG);
347
      }
348
    }
349
    drupal_set_message(theme('ldap_authentication_message_not_authenticated', array(
350
      'message' => t('You were not authenticated by the server. You may log in with your credentials below.'),
351
      )), 'error');
352
    if ($detailed_watchdog_log) {
353
      watchdog('ldap_authentication', 'ldap_sso_user_login_sso.no_remote_user.drupal_goto user/login', $watchdog_tokens, WATCHDOG_DEBUG);
354
    }
355
    drupal_goto('user/login');
356
  }
357
}
358

    
359

    
360
/**
361
 * Used to mock $_SERVER, $_SESSION, etc globals for simpletests.
362
 *
363
 * @param string $global_type
364
 *   _SERVER, _ENV, _COOKIE, _GET, _POST, _REQUEST.
365
 * @param string $key
366
 *   Such as 'SERVER_ADDR', 'SERVER_PROTOCOL', etc.
367
 * @param bool $only_mock_values
368
 *   Don't get actual values when mock values don't exist.
369
 *
370
 * @return mixed
371
 *   ldap_simpletest_globals variable for global and key or $_SERVER[][],
372
 *   $_ENV[][], etv value if not in a simpletest or mock variable not available.
373
 */
374
function ldap_servers_get_globals($global_type, $key, $only_mock_values = FALSE) {
375
  $simpletest_globals = variable_get('ldap_simpletest_globals', array());
376
  $simpletest = variable_get('ldap_simpletest', FALSE);
377

    
378
  if ($simpletest && (isset($simpletest_globals[$global_type][$key]) || $only_mock_values)) {
379
    return ($simpletest_globals[$global_type][$key]) ? $simpletest_globals[$global_type][$key] : NULL;
380
  }
381
  else {
382
    return (isset($GLOBALS[$global_type][$key]) && !$only_mock_values) ? $GLOBALS[$global_type][$key] : NULL;
383
  }
384

    
385
}
386

    
387
/**
388
 * Set globals.
389
 *
390
 * @param string $global_type
391
 *   _SERVER, _ENV, _COOKIE, _GET, _POST, _REQUEST.
392
 * @param string $key
393
 *   Such as 'SERVER_ADDR', 'SERVER_PROTOCOL', etc.
394
 * @param string $value
395
 *   The value to be set.
396
 */
397
function ldap_servers_set_globals($global_type, $key, $value) {
398
  $simpletest_globals = variable_get('ldap_simpletest_globals', array());
399
  $simpletest = variable_get('ldap_simpletest', FALSE);
400
  if ($simpletest) {
401
    $simpletest_globals[$global_type][$key] = $value;
402
    variable_set('ldap_simpletest_globals', $simpletest_globals);
403
  }
404
  else {
405
    $GLOBALS[$global_type][$key] = $value;
406
  }
407

    
408
}
409

    
410
/**
411
 * Delete globals.
412
 *
413
 * @param string $global_type
414
 *   _SERVER, _ENV, _COOKIE, _GET, _POST, _REQUEST.
415
 * @param string $key
416
 *   Such as 'SERVER_ADDR', 'SERVER_PROTOCOL', etc.
417
 * @param bool $only_mock_values
418
 *   Don't get actual values when mock values don't exist.
419
 */
420
function ldap_servers_delete_globals($global_type, $key, $only_mock_values = FALSE) {
421
  $simpletest_globals = variable_get('ldap_simpletest_globals', array());
422
  $simpletest = variable_get('ldap_simpletest', FALSE);
423
  if ($simpletest && isset($simpletest_globals[$global_type][$key])) {
424
    unset($simpletest_globals[$global_type][$key]);
425
    variable_set('ldap_simpletest_globals', $simpletest_globals);
426
  }
427
  elseif (!$only_mock_values && isset($GLOBALS[$global_type][$key])) {
428
    unset($GLOBALS[$global_type][$key]);
429
  }
430

    
431
}