Project

General

Profile

Paste
Download (41.7 KB) Statistics
| Branch: | Revision:

root / drupal7 / sites / all / modules / cas / cas.module @ 082b75eb

1
<?php
2

    
3
/**
4
 * @file
5
 * Enables users to authenticate via a Central Authentication Service (CAS)
6
 * Cas will currently work if the auto registration is turned on and will
7
 * create user accounts automatically.
8
 */
9

    
10
define('CAS_NO_LINK', 0);
11
define('CAS_ADD_LINK', 1);
12
define('CAS_MAKE_DEFAULT', 2);
13
define('CAS_REDIRECT', 3);
14
define('CAS_LOGIN_INVITE_DEFAULT', 'Log in using CAS');
15
define('CAS_LOGIN_DRUPAL_INVITE_DEFAULT', 'Cancel CAS login');
16
define('CAS_LOGIN_REDIR_MESSAGE', 'You will be redirected to the secure CAS login page.');
17
define('CAS_EXCLUDE', 'services/*');
18

    
19
// Frequency of CAS Gateway checking.
20
define('CAS_CHECK_NEVER', -2);
21
define('CAS_CHECK_ONCE', -1);
22
define('CAS_CHECK_ALWAYS', 0);
23

    
24
/**
25
 * Implements hook_init().
26
 *
27
 * Traps a page load to see if authentication is required.
28
 */
29
function cas_init() {
30
  global $user;
31

    
32
  if (module_exists('cas_test') && arg(0) == 'cas_test') {
33
    // We are destined for a page handled by the cas_test module, so do not
34
    // do any processing here. Necessary for CAS gateway tests.
35
    return;
36
  }
37
  elseif (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE === 'install') {
38
    // Do nothing on profile install.
39
    return;
40
  }
41

    
42
  // Process a single-sign out request.
43
  _cas_single_sign_out_check();
44

    
45
  // If a user is not logged in, consider using CAS authentication.
46
  if (!$user->uid) {
47
    $force_authentication = _cas_force_login();
48
    $check_authentication = _cas_allow_check_for_login();
49
    $request_type = $_SERVER['REQUEST_METHOD'];
50
    $perform_login_check = $force_authentication || ($check_authentication && ($request_type == 'GET'));
51
    if ($perform_login_check) {
52
      cas_login_check($force_authentication);
53
    }
54
  }
55
}
56

    
57
/**
58
 * Checks to see if the user needs to be logged in.
59
 *
60
 * @param $force_authentication
61
 *   If TRUE, require that the user be authenticated with the CAS server
62
 *   before proceeding. Otherwise, check with the CAS server to see if the
63
 *   user is already logged in.
64
 */
65
function cas_login_check($force_authentication = TRUE) {
66
  global $user;
67
  if ($user->uid) {
68
    //Don't Login  because we already are
69
    return;
70
  }
71

    
72
  if (!cas_phpcas_load()) {
73
    // No need to print a message, as the user will already see the failed
74
    // include_once calls.
75
    return;
76
  }
77

    
78
  // Start a drupal session
79
  drupal_session_start();
80
  _cas_single_sign_out_save_ticket();  // We use this later for CAS 3 logoutRequests
81

    
82
  // Initialize phpCAS if possible, otherwise just back out.
83
  if (cas_phpcas_init() === FALSE) {
84
    return;
85
  }
86

    
87
  // We're going to try phpCAS auth test
88
  if ($force_authentication) {
89
    try {
90
      phpCAS::forceAuthentication();
91
    }
92
    catch (CAS_AuthenticationException $e) {
93
      drupal_set_message(t('Error authenticating with CAS. Please try again or contact your website administrator if the problem persists.'), 'error');
94
      watchdog_exception('cas', $e);
95
      // We have to redirect the user somewhere else because the CAS exception
96
      // will have written error details directly to stdout, and we don't want
97
      // to show anything like that to the user.
98
      // This bring users to the homepage or to the page set in destination
99
      // param.
100
      drupal_goto('');
101
    }
102
  }
103
  else {
104
    $logged_in = phpCAS::checkAuthentication();
105

    
106
    // We're done cause we're not logged in.
107
    if (!$logged_in) {
108
      return;
109
    }
110
  }
111

    
112
  // Build the cas_user object and allow modules to alter it.
113
  $cas_user = array(
114
    'name' => phpCAS::getUser(),
115
    'login' => TRUE,
116
    'register' => variable_get('cas_user_register', TRUE),
117
    'attributes' => cas_phpcas_attributes(),
118
  );
119
  drupal_alter('cas_user', $cas_user);
120

    
121
  // Bail out if a module denied login access for this user or unset the user
122
  // name.
123
  if (empty($cas_user['login']) || empty($cas_user['name'])) {
124
    // Only set a warning if we forced login.
125
    if ($force_authentication) {
126
      drupal_set_message(t('The user account %name is not available on this site.', array('%name' => $cas_user['name'])), 'error');
127
    }
128
    return;
129
  }
130

    
131
  // Proceed with the login process, using the altered CAS username.
132
  $cas_name = $cas_user['name'];
133

    
134
  // blocked user check
135
  $blocked = FALSE;
136
  if (_cas_external_user_is_blocked($cas_name)) {
137
      $blocked = 'The username %cas_name has been blocked.';
138
  }
139
  // @todo The D7 equivalent here must have been renamed.
140
  // elseif (drupal_is_denied('user', $cas_name)) {
141
  //   // denied by access controls
142
  //   return 'The name %cas_name is a reserved username.';
143
  // }
144

    
145
  if ($blocked) {
146
    // Only display error messages only if the user intended to log in.
147
    if ($force_authentication) {
148
      watchdog('cas', $blocked, array('%cas_name' => $cas_name), WATCHDOG_WARNING);
149
      drupal_set_message(t($blocked, array('%cas_name' => $cas_name)), 'error');
150
    }
151
    return;
152
  }
153

    
154
  $account = cas_user_load_by_name($cas_name);
155

    
156
  // Automatic user registration.
157
  if (!$account && $cas_user['register']) {
158
    // No account could be found and auto registration is enabled, so attempt
159
    // to register a new user.
160
    $account = cas_user_register($cas_name);
161
    if (!$account) {
162
      // The account could not be created, set a message.
163
      if ($force_authentication) {
164
        drupal_set_message(t('A new account could not be created for %cas_name. The username is already in use on this site.', array('%cas_name' => $cas_name)), 'error');
165
      }
166
      return;
167
    }
168
  }
169

    
170
  // final check to make sure we have a good user
171
  if ($account && $account->uid > 0) {
172
    // Save the altered CAS name for future use.
173
    $_SESSION['cas_name'] = $cas_name;
174

    
175
    $cas_first_login = !$account->login;
176

    
177
    // Save single sign out information
178
    if (!empty($_SESSION['cas_ticket'])) {
179
      _cas_single_sign_out_save_token($account);
180
    }
181

    
182
    // Populate $edit with some basic properties.
183
    $edit['cas_user'] = $cas_user;
184
    $edit['roles'] = $account->roles + cas_roles();
185
    if (module_exists('persistent_login') && !empty($_SESSION['cas_remember'])) {
186
      $edit['values']['persistent_login'] = 1;
187
    }
188
    // Allow other modules to make their own custom changes.
189
    cas_user_module_invoke('presave', $edit, $account);
190

    
191
    // Save the user account and log the user in.
192
    $user = user_save($account, $edit);
193
    user_login_finalize($edit);
194

    
195
    drupal_set_message(t(variable_get('cas_login_message', 'Logged in via CAS as %cas_username.'), array('%cas_username' => format_username($user))));
196
    if (!empty($edit['persistent_login'])) {
197
      drupal_set_message(t('You will remain logged in on this computer even after you close your browser.'));
198
    }
199

    
200
    _cas_redirect_after_login($cas_first_login);
201

    
202
  }
203
  else {
204
    $user = drupal_anonymous_user();
205
    unset($_SESSION['phpCAS']);
206

    
207
    // Only display error messages only if the user intended to log in.
208
    if ($force_authentication) {
209
      drupal_set_message(t('No account found for %cas_name.', array('%cas_name' => $cas_name)), 'error');
210
    }
211
  }
212
}
213

    
214
/**
215
 * Loads the phpCAS library.
216
 *
217
 * @param $path
218
 *   Attempt to load phpCAS using this path. If omitted, phpCAS will be loaded
219
 *   using Libraries API or the configured value.
220
 *
221
 * @return
222
 *   The phpCAS version if the phpCAS was successfully loaded, FALSE otherwise.
223
 */
224
function cas_phpcas_load($path = NULL) {
225
  if (!isset($path)) {
226
    if (module_exists('libraries')) {
227
      $path = libraries_get_path('CAS');
228
    }
229
    else {
230
      $path = variable_get('cas_library_dir', 'CAS');
231
    }
232
  }
233

    
234
  // Build the name of the file to load.
235
  if ($path != '') {
236
    $path = rtrim($path, '/') . '/';
237
  }
238
  $filename = $path . 'CAS.php';
239

    
240
  include_once($filename);
241

    
242
  if (!defined('PHPCAS_VERSION') || !class_exists('phpCAS')) {
243
    // The file could not be loaded successfully.
244
    return FALSE;
245
  }
246
  return PHPCAS_VERSION;
247
}
248

    
249
/**
250
 * Initialize phpCAS.
251
 *
252
 * Will load phpCAS if necessary.
253
 */
254
function cas_phpcas_init() {
255
  if (!defined('PHPCAS_VERSION') || !class_exists('phpCAS')) {
256
    cas_phpcas_load();
257
  }
258

    
259
  $initialized = &drupal_static(__FUNCTION__, FALSE);
260
  if ($initialized) {
261
    // phpCAS cannot be initialized twice. If you need to force this function
262
    // to run again, call drupal_static_reset('cas_phpcas_init') first.
263
    return;
264
  }
265
  $initialized = TRUE;
266

    
267
  // Variable set
268
  $server_version    = (string)variable_get('cas_version', '3.0');
269
  $server_cas_server = (string)variable_get('cas_server', '');
270
  $server_port       = (int)variable_get('cas_port', '443');
271
  $server_uri        = (string)variable_get('cas_uri', '');
272
  $cas_cert          = (string)variable_get('cas_cert', '');
273
  $debug_file        = (string)variable_get('cas_debugfile', '');
274

    
275
  // Back out if there's no configured CAS server hostname. This is the
276
  // minimum required configuration to initialize phpCAS.
277
  if (empty($server_cas_server)) {
278
    watchdog('cas', 'Unable to initialize phpCAS because no CAS server hostname has been configured.', array(), WATCHDOG_ERROR);
279
    return FALSE;
280
  }
281

    
282
  if ($debug_file != '') {
283
    phpCAS::setDebug($debug_file);
284
  }
285
  $start_session = (boolean)FALSE;
286
  if (variable_get('cas_proxy', 0)) {
287
    phpCAS::proxy($server_version, $server_cas_server, $server_port, $server_uri, $start_session);
288
    $cas_pgt_storage_path = variable_get('cas_pgtpath', '');
289
    if ($cas_pgt_storage_path != '') {
290
      if (version_compare(PHPCAS_VERSION, '1.3', '>=')) {
291
        phpCAS::setPGTStorageFile($cas_pgt_storage_path);
292
      }
293
      else {
294
        $cas_pgt_format = variable_get('cas_pgtformat', 'plain');
295
        phpCAS::setPGTStorageFile($cas_pgt_format, $cas_pgt_storage_path);
296
      }
297
    }
298
  }
299
  else {
300
    phpCAS::client($server_version, $server_cas_server, $server_port, $server_uri, $start_session);
301
  }
302

    
303
  //Add CAS proxy lists allowed
304
  $proxy_list = variable_get('cas_proxy_list', '');
305
  if ($proxy_list) {
306
    $proxy_list = explode("\n", $proxy_list);
307
    phpCAS::allowProxyChain(new CAS_ProxyChain($proxy_list));
308
  }
309

    
310
  // force CAS authentication
311
  if ($cas_cert = variable_get('cas_cert', '')) {
312
    phpCAS::setCasServerCACert($cas_cert);
313
  }
314
  else {
315
    phpCAS::setNoCasServerValidation();
316
  }
317

    
318
  phpCAS::setFixedServiceURL(url(current_path(), array('query' => drupal_get_query_parameters(), 'absolute' => TRUE)));
319
  phpCAS::setCacheTimesForAuthRecheck((int) variable_get('cas_check_frequency', CAS_CHECK_NEVER));
320

    
321
  // Allow other modules to call phpCAS routines. We do not call
322
  // drupal_alter() since there are no parameters to pass.
323
  module_invoke_all('cas_phpcas_alter');
324
}
325

    
326

    
327
/**
328
 * Implements hook_permission().
329
 */
330
function cas_permission() {
331
  return array(
332
    'administer cas' => array(
333
      'title' => t('Administer CAS'),
334
      'description' => t('Configure CAS server, default CAS user roles, login/logout redirection, and other settings.'),
335
      'restrict access' => TRUE,
336
    )
337
  );
338
}
339

    
340
/**
341
 * Implements hook_help().
342
 */
343
function cas_help($section) {
344
  switch ($section) {
345
    case 'admin/help#cas':
346
      return t("Allows users to authenticate via a Central Authentication Service.");
347
  }
348
}
349

    
350
/**
351
 * Implements hook_menu().
352
 */
353
function cas_menu() {
354
  global $user;
355
  $items = array();
356
  //cas_login_check();
357
  $items['admin/config/people/cas'] = array(
358
    'title' => 'CAS settings',
359
    'description' => 'Configure central authentication services',
360
    'page callback' => 'drupal_get_form',
361
    'page arguments' => array('cas_admin_settings'),
362
    'access arguments' => array('administer cas'),
363
    'type' => MENU_NORMAL_ITEM,
364
    'file' => 'cas.admin.inc',
365
  );
366
  $items['admin/config/people/cas/settings'] = array(
367
    'title' => 'CAS',
368
    'type' => MENU_DEFAULT_LOCAL_TASK,
369
    'weight' => -10,
370
  );
371
  $items['admin/people/cas/create'] = array(
372
    'title' => 'Add CAS user(s)',
373
    'page callback' => 'drupal_get_form',
374
    'page arguments' => array('cas_add_user_form'),
375
    'access arguments' => array('administer users'),
376
    'type' => MENU_LOCAL_ACTION,
377
    'file' => 'cas.user.inc',
378
    'tab_parent' => 'admin/people',
379
    'weight' => 1,
380
  );
381
  $items['user/%user/cas'] = array(
382
    'title' => 'CAS',
383
    'page callback' => 'cas_user_identities',
384
    'page arguments' => array(1),
385
    'access arguments' => array('administer users'),
386
    'type' => MENU_LOCAL_TASK,
387
    'file' => 'cas.pages.inc',
388
    'tab_parent' => 'user/%/edit',
389
    'weight' => 1,
390
  );
391
  $items['user/%user/cas/delete'] = array(
392
    'title' => 'Delete CAS username',
393
    'page callback' => 'drupal_get_form',
394
    'page arguments' => array('cas_user_delete_form', 1),
395
    'access arguments' => array('administer users'),
396
    'file' => 'cas.pages.inc',
397
  );
398
  $items['cas'] = array(
399
    'path' => 'cas',
400
    'title' => 'CAS Login',
401
    'page callback' => 'cas_login_page',
402
    'access callback' => 'user_is_anonymous',
403
    'type' => MENU_SUGGESTED_ITEM,
404
  );
405
  $items['caslogout'] = array(
406
    'title' => 'CAS Logout',
407
    'page callback' => 'cas_logout',
408
    'access callback' => 'cas_user_is_logged_in',
409
    'type' => MENU_SUGGESTED_ITEM,
410
  );
411
  return $items;
412
}
413

    
414
/**
415
 * Implements hook_cron().
416
 */
417
function cas_cron() {
418
  // Clear old single logout session mapping data.
419
  $max_days = (int) variable_get('cas_single_logout_session_lifetime', 25);
420
  $seconds_in_day = 86400;
421
  $seconds = $max_days * $seconds_in_day;
422
  if ($seconds > 0) {
423
    db_delete('cas_login_data')
424
      ->condition('created', time() - $seconds, '<=')
425
      ->execute();
426
  }
427
}
428

    
429
function cas_user_is_logged_in() {
430
  return user_is_logged_in() || !empty($_SESSION['phpCAS']['user']);
431
}
432

    
433
/**
434
 * Implements hook_menu_site_status_alter().
435
 */
436
function cas_menu_site_status_alter(&$menu_site_status, $path) {
437
  if (user_is_logged_in() && strtolower($path) == 'cas') {
438
    // If user is logged in, redirect to '<front>' instead of giving 403.
439
    drupal_goto('');
440
  }
441
}
442

    
443
/**
444
 * Implements hook_menu_link_alter().
445
 *
446
 * Flag this link as needing alter at display time.
447
 * @see cas_translated_menu_link_alter()
448
 */
449
function cas_menu_link_alter(&$item) {
450
  $link_path = strtolower($item['link_path']);
451
  if ($link_path == 'cas' || $link_path == 'caslogout') {
452
    $item['options']['alter'] = TRUE;
453
  }
454
}
455

    
456
/**
457
 * Implements hook_translated_menu_item_alter().
458
 *
459
 * Append dynamic query 'destination' to CAS menu links.
460
 */
461
function cas_translated_menu_link_alter(&$item) {
462
  $href = strtolower($item['href']);
463
  $append_destination = $href == 'cas' || ($href == 'caslogout' && !variable_get('cas_logout_destination', ''));
464
  if ($append_destination) {
465
    // Don't overwrite existing query params that may exist in the menu link.
466
    if (isset($item['localized_options']['query'])) {
467
      $menu_link_query_params = array_merge(drupal_get_destination(), $item['localized_options']['query']);
468
    }
469
    else {
470
      $menu_link_query_params = drupal_get_destination();
471
    }
472
    $item['localized_options']['query'] = $menu_link_query_params;
473
  }
474
}
475

    
476
/**
477
 * Implements hook_user_operations().
478
 */
479
function cas_user_operations($form = array(), $form_state = array()) {
480
  $operations['cas_create'] = array(
481
    'label' => t('Create CAS username'),
482
    'callback' => 'cas_user_operations_create_username',
483
  );
484
  $operations['cas_remove'] = array(
485
    'label' => t('Remove CAS usernames'),
486
    'callback' => 'cas_user_operations_remove_usernames',
487
  );
488
  return $operations;
489
}
490

    
491
/**
492
 * Callback function to assign a CAS username to the account.
493
 *
494
 * @param $uids
495
 *   An array of user ids. For each account, a CAS username is created with
496
 *   the same name as the Drupal username.
497
 *
498
 * @see cas_user_operations()
499
 */
500
function cas_user_operations_create_username($uids) {
501
  $accounts = user_load_multiple($uids);
502
  foreach ($accounts as $account) {
503
    $count = db_select('cas_user', 'c')
504
      ->condition('cas_name', $account->name)
505
      ->condition('uid', $account->uid, '<>')
506
      ->countQuery()->execute()->fetchfield();
507
    if ($count) {
508
      drupal_set_message(t('CAS username %username already in use.', array('%username' => $account->name)), 'error');
509
      continue;
510
    }
511
    db_merge('cas_user')
512
      ->key(array('cas_name' => $account->name))
513
      ->fields(array('uid' => $account->uid))
514
      ->execute();
515
  }
516
}
517

    
518
/**
519
 * Callback function to remove CAS usernames from the account.
520
 *
521
 * @param $uids
522
 *   An array of user ids. For each account, all CAS usernames are removed.
523
 *
524
 * @see cas_user_operations()
525
 */
526
function cas_user_operations_remove_usernames($uids) {
527
  db_delete('cas_user')
528
    ->condition('uid', $uids, 'IN')
529
    ->execute();
530
}
531

    
532
/**
533
 * Implements hook_admin_paths().
534
 */
535
function cas_admin_paths() {
536
  $paths = array(
537
    'user/*/cas' => TRUE,
538
    'user/*/cas/delete/*' => TRUE,
539
  );
540
  return $paths;
541
}
542

    
543
/**
544
 * Implements hook_user_load().
545
 *
546
 * Adds an associative array 'cas_names' to each user. The array keys are
547
 * unique authentication mapping ids, with CAS usernames as the values.
548
 */
549
function cas_user_load($users) {
550
  foreach (array_keys($users) as $uid) {
551
    $users[$uid]->cas_names = array();
552
  }
553
  $result = db_query('SELECT aid, uid, cas_name FROM {cas_user} WHERE uid IN (:uids)', array(':uids' => array_keys($users)));
554
  foreach ($result as $record) {
555
    $users[$record->uid]->cas_names[$record->aid] = $record->cas_name;
556
  }
557
  foreach (array_keys($users) as $uid) {
558
    $users[$uid]->cas_name = reset($users[$uid]->cas_names);
559
  }
560
}
561

    
562
/**
563
 * Implements hook_user_insert().
564
 *
565
 * When a user is created, record their CAS username if provided.
566
 */
567
function cas_user_insert(&$edit, $account, $category) {
568
  if (!empty($edit['cas_name'])) {
569
    db_insert('cas_user')
570
      ->fields(array(
571
        'cas_name' => $edit['cas_name'],
572
        'uid' => $account->uid,
573
      ))
574
      ->execute();
575
  }
576
  // Update $account to reflect changes.
577
  $users = array($account->uid => $account);
578
  cas_user_load($users);
579
}
580

    
581
/**
582
 * Implements hook_user_update().
583
 *
584
 * When a user is updated, change their CAS username if provided.
585
 */
586
function cas_user_update(&$edit, $account, $category) {
587
  if (!array_key_exists('cas_name', $edit)) {
588
    // If the cas_name key is not provided, there is nothing to do.
589
    return;
590
  }
591
  $cas_name = $edit['cas_name'];
592

    
593
  // See if the user currently has any CAS names.
594
  reset($account->cas_names);
595
  if ($aid = key($account->cas_names)) {
596
    // The user already has CAS username(s) set.
597
    if (empty($cas_name)) {
598
      // Remove a CAS username.
599
      db_delete('cas_user')
600
        ->condition('uid', $account->uid)
601
        ->condition('aid', $aid)
602
        ->execute();
603
    }
604
    else {
605
      // Change a CAS username.
606
      if ($cas_name != $account->cas_names[$aid]) {
607
        db_update('cas_user')
608
          ->fields(array('cas_name' => $cas_name))
609
          ->condition('uid', $account->uid)
610
          ->condition('aid', $aid)
611
          ->execute();
612
      }
613
    }
614
  }
615
  else {
616
    // No current CAS usernames.
617
    if (!empty($cas_name)) {
618
      // Add a CAS username.
619
      db_insert('cas_user')
620
        ->fields(array(
621
          'uid' => $account->uid,
622
          'cas_name' => $cas_name,
623
        ))
624
        ->execute();
625
    }
626
  }
627
  // Update $account to reflect changes.
628
  $users = array($account->uid => $account);
629
  cas_user_load($users);
630
}
631

    
632
/**
633
 * Implement hook_user_delete().
634
 *
635
 * When a CAS user is deleted, we need to clean up the entry in {cas_user}.
636
 */
637
function cas_user_delete($account) {
638
  db_delete('cas_user')
639
    ->condition('uid', $account->uid)
640
    ->execute();
641
}
642

    
643
/**
644
 * Fetch a user object by CAS name.
645
 *
646
 * @param $cas_name
647
 *   The name of the CAS user.
648
 * @param $alter
649
 *   If TRUE, run the CAS username through hook_cas_user_alter() before
650
 *   loading the account.
651
 * @param $reset
652
 *   TRUE to reset the internal cache and load from the database; FALSE
653
 *   (default) to load from the internal cache, if set.
654
 *
655
 * @return
656
 *   A fully-loaded $user object upon successful user load or FALSE if user
657
 *   cannot be loaded.
658
 */
659
function cas_user_load_by_name($cas_name, $alter = FALSE, $reset = FALSE) {
660
  if ($alter) {
661
    $cas_user = array(
662
      'name' => $cas_name,
663
      'login' => TRUE,
664
      'register' => FALSE,
665
    );
666
    drupal_alter('cas_user', $cas_user);
667
    $cas_name = $cas_user['name'];
668
  }
669

    
670
  $uid = db_select('cas_user')->fields('cas_user', array('uid'))->condition('cas_name', db_like($cas_name), 'LIKE')->range(0, 1)->execute()->fetchField();
671
  if ($uid) {
672
    return user_load($uid, $reset);
673
  }
674
  return FALSE;
675
}
676

    
677
/**
678
 * This is the page callback for the /cas page, which is used only to
679
 * trigger a forced CAS authentication.
680
 *
681
 * In almost all cases, the user will have been redirected before even
682
 * hitting this page (see hook_init implementation). But as a stop gap
683
 * just redirect to the homepage.
684
 */
685
function cas_login_page() {
686
  drupal_goto('');
687
}
688

    
689
/**
690
 * Logs a user out of Drupal and then out of CAS.
691
 *
692
 * This function does not return, but instead immediately redirects the user
693
 * to the CAS server to complete the CAS logout process.
694
 *
695
 * Other modules intending to call this from their implementation of
696
 * hook_user_logout() will need to pass $invoke_hook = FALSE to avoid an
697
 * infinite recursion. WARNING: since this function does not return, any
698
 * later implementations of hook_user_logout() will not run. You may wish to
699
 * adjust the hook execution order using hook_module_implements_alter().
700
 *
701
 * @param $invoke_hook
702
 *   If TRUE, invoke hook_user_logout() and save a watchdog message indicating
703
 *   that the user has logged out.
704
 */
705
function cas_logout($invoke_hook = TRUE) {
706
  global $user;
707

    
708
  // Build the logout URL.
709
  cas_phpcas_init();
710

    
711
  if (isset($_GET['destination']) && !url_is_external($_GET['destination'])) {
712
    // Add destination override so that a destination can be specified on the
713
    // logout link, e.g., caslogout?destination=http://foo.bar.com/foobar. We do
714
    // not allow absolute URLs to be passed via $_GET, as this can be an attack
715
    // vector.
716
    $destination = $_GET['destination'];
717
  }
718
  else {
719
    $destination = variable_get('cas_logout_destination', '');
720
  }
721

    
722
  //Make it an absolute url.  This will also convert <front> to the front page.
723
  if ($destination) {
724
    $destination_url = url($destination, array('absolute' => TRUE));
725
    $options = array(
726
      'service' => $destination_url,
727
      'url' => $destination_url,
728
    );
729
  }
730
  else {
731
    $options = array();
732
  }
733

    
734
  // Mimic user_logout().
735
  if ($invoke_hook) {
736
    watchdog('user', 'Session closed for %name.', array('%name' => format_username($user)));
737
    module_invoke_all('user_logout', $user);
738
  }
739

    
740
  // phpCAS automatically calls session_destroy().
741
  phpCAS::logout($options);
742
}
743

    
744
/**
745
 * Implements hook_block_info().
746
 */
747
function cas_block_info() {
748
  $blocks['login']['info'] = t('CAS login');
749
  // Not worth caching.
750
  $blocks['login']['cache'] = DRUPAL_NO_CACHE;
751

    
752
  return $blocks;
753
}
754

    
755
/**
756
 * Implements hook_block_view().
757
 */
758
function cas_block_view($delta = '') {
759
  global $user;
760

    
761
  $block = array();
762

    
763
  switch ($delta) {
764
    case 'login':
765
      // For usability's sake, avoid showing two login forms on one page.
766
      if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
767
        $block['subject'] = t('User login');
768
        $block['content'] = drupal_get_form('cas_login_block');
769
      }
770
      return $block;
771
  }
772
}
773

    
774
/**
775
 * Login form for the CAS login block.
776
 */
777
function cas_login_block($form) {
778
  $form['#action'] = url('cas', array('query' => drupal_get_destination()));
779
  $form['#id'] = 'cas-login-form';
780

    
781
  $form['cas_login_redirection_message'] = array(
782
    '#type' => 'item',
783
    '#markup' => t(variable_get('cas_login_redir_message', CAS_LOGIN_REDIR_MESSAGE)),
784
    '#weight' => -1,
785
  );
786
  $form['actions'] = array('#type' => 'actions');
787
  $form['actions']['submit'] = array(
788
    '#type' => 'submit',
789
    '#value' => t(variable_get('cas_login_invite', CAS_LOGIN_INVITE_DEFAULT)),
790
  );
791

    
792
  return $form;
793
}
794

    
795
/**
796
 * Determine if we should automatically check if the user is authenticated.
797
 *
798
 * This implements part of the CAS gateway feature.
799
 * @see phpCAS::checkAuthentication()
800
 *
801
 * @return
802
 *   TRUE if we should query the CAS server to see if the user is already
803
 *   authenticated, FALSE otherwise.
804
 */
805
function _cas_allow_check_for_login() {
806
  // Do not process in maintenance mode.
807
  if (variable_get('maintenance_mode', 0)) {
808
    return FALSE;
809
  }
810

    
811
  if (variable_get('cas_check_frequency', CAS_CHECK_NEVER) == CAS_CHECK_NEVER) {
812
    // The user has disabled the feature.
813
    return FALSE;
814
  }
815

    
816
  // Check to see if we've got a search bot.
817
  if (isset($_SERVER['HTTP_USER_AGENT'])) {
818
    $crawlers = array(
819
      'Google',
820
      'msnbot',
821
      'Rambler',
822
      'Yahoo',
823
      'AbachoBOT',
824
      'accoona',
825
      'AcoiRobot',
826
      'ASPSeek',
827
      'CrocCrawler',
828
      'Dumbot',
829
      'FAST-WebCrawler',
830
      'GeonaBot',
831
      'Gigabot',
832
      'Lycos',
833
      'MSRBOT',
834
      'Scooter',
835
      'AltaVista',
836
      'IDBot',
837
      'eStyle',
838
      'Scrubby',
839
      'gsa-crawler',
840
      );
841
    // Return on the first find.
842
    foreach ($crawlers as $c) {
843
      if (stripos($_SERVER['HTTP_USER_AGENT'], $c) !== FALSE) {
844
        return FALSE;
845
      }
846
    }
847
  }
848

    
849
  // Do not force login for XMLRPC, Cron, or Drush.
850
  if (stristr($_SERVER['SCRIPT_FILENAME'], 'xmlrpc.php')) {
851
    return FALSE;
852
  }
853
  if (stristr($_SERVER['SCRIPT_FILENAME'], 'cron.php')) {
854
    return FALSE;
855
  }
856
  if (stristr($_SERVER['SCRIPT_FILENAME'], 'drush')) {
857
    return FALSE;
858
  }
859
  if (!empty($_SERVER['argv'][0]) && stristr($_SERVER['argv'][0], 'drush')) {
860
    return FALSE;
861
  }
862

    
863
  // Test against exclude pages.
864
  if ($pages = variable_get('cas_exclude', CAS_EXCLUDE)) {
865
    $path = drupal_get_path_alias($_GET['q']);
866
    if (drupal_match_path($path, $pages)) {
867
      return FALSE;
868
    }
869
  }
870

    
871
  return TRUE;
872
}
873

    
874
/**
875
 * Determine if we should require the user be authenticated.
876
 *
877
 * @return
878
 *   TRUE if we should require the user be authenticated, FALSE otherwise.
879
 */
880
function _cas_force_login() {
881
  // The 'cas' page is a shortcut to force authentication.
882
  if (strtolower(arg(0)) == 'cas') {
883
    return TRUE;
884
  }
885

    
886
  // Do not process in maintenance mode.
887
  if (variable_get('maintenance_mode', 0)) {
888
    return FALSE;
889
  }
890

    
891
  // Do not force login for XMLRPC, Cron, or Drush.
892
  if (stristr($_SERVER['SCRIPT_FILENAME'], 'xmlrpc.php')) {
893
    return FALSE;
894
  }
895
  if (stristr($_SERVER['SCRIPT_FILENAME'], 'cron.php')) {
896
    return FALSE;
897
  }
898
  if (function_exists('drush_verify_cli') && drush_verify_cli()) {
899
    return FALSE;
900
  }
901

    
902
  // Excluded page do not need login.
903
  if ($pages = variable_get('cas_exclude', CAS_EXCLUDE)) {
904
    $path = drupal_get_path_alias($_GET['q']);
905
    if (drupal_match_path($path, $pages)) {
906
      return FALSE;
907
    }
908
  }
909

    
910
  // Set the default behavior.
911
  $force_login = variable_get('cas_access', 0);
912

    
913
  // If we match the specified paths, reverse the behavior.
914
  if ($pages = variable_get('cas_pages', '')) {
915
    $path = drupal_get_path_alias($_GET['q']);
916
    if (drupal_match_path($path, $pages)) {
917
      $force_login = !$force_login;
918
    }
919
  }
920

    
921
  return $force_login;
922
}
923
/**
924
 * Implements hook_form_alter().
925
 *
926
 * Overrides specific from settings based on user policy.
927
 */
928
function cas_form_alter(&$form, &$form_state, $form_id) {
929
  // Special handling of the user login page when the CAS login form is set to
930
  // redirect.
931
  if ($form_id == 'user_login' && variable_get('cas_login_form', CAS_NO_LINK) == CAS_REDIRECT) {
932
    drupal_goto('cas');
933
  }
934

    
935
  switch ($form_id) {
936
    case 'user_login':
937
    case 'user_login_block':
938
      if (variable_get('cas_login_form', CAS_NO_LINK) != CAS_NO_LINK) {
939
        $form['#attached']['css'][] = drupal_get_path('module', 'cas') . '/cas.css';
940
        $form['#attached']['js'][] = drupal_get_path('module', 'cas') . '/cas.js';
941

    
942
        if (!empty($form_state['input']['cas_identifier'])) {
943
          $form['name']['#required'] = FALSE;
944
          $form['pass']['#required'] = FALSE;
945
          unset($form['#validate']);
946
          $form['#submit'] = array('cas_login_submit');
947
        }
948

    
949
        $items = array();
950
        $items[] = array(
951
          'data' => l(t(variable_get('cas_login_invite', CAS_LOGIN_INVITE_DEFAULT)), '#'),
952
          'class' => array('cas-link'),
953
        );
954

    
955
        $items[] = array(
956
          'data' => l(t(variable_get('cas_login_drupal_invite', CAS_LOGIN_DRUPAL_INVITE_DEFAULT)), '#'),
957
          'class' => array('uncas-link'),
958
        );
959

    
960
        $form['cas_links'] = array(
961
          '#theme' => 'item_list',
962
          '#items' => $items,
963
          '#attributes' => array('class' => array('cas-links')),
964
          '#weight' => 101,
965
        );
966

    
967
        $form['links']['#weight'] = 2;
968

    
969
        $form['cas_login_redirection_message'] = array(
970
          '#type' => 'item',
971
          '#markup' => t(variable_get('cas_login_redir_message', CAS_LOGIN_REDIR_MESSAGE)),
972
          '#weight' => -1,
973
        );
974

    
975
        $form['cas_identifier'] = array(
976
          '#type' => 'checkbox',
977
          '#title' => t(variable_get('cas_login_invite', CAS_LOGIN_INVITE_DEFAULT)),
978
          '#default_value' => variable_get('cas_login_form', CAS_NO_LINK) == CAS_MAKE_DEFAULT,
979
          '#weight' => -1,
980
          '#description' => t(variable_get('cas_login_redir_message', CAS_LOGIN_REDIR_MESSAGE)),
981
        );
982
        $form['cas.return_to'] = array('#type' => 'hidden', '#value' => user_login_destination());
983
      }
984
      break;
985

    
986
    case 'user_profile_form':
987
      $account = $form['#user'];
988
      if (user_access('administer users')) {
989
        // The user is an administrator, so add fields to allow changing the
990
        // CAS username(s) associated with the account.
991
        $cas_names = $account->cas_names;
992
        $aids = array_keys($cas_names);
993

    
994
        $element = array(
995
          '#type' => 'textfield',
996
          '#title' => t('CAS username'),
997
          '#default_value' => array_shift($cas_names),
998
          '#cas_user_aid' => array_shift($aids),
999
          '#description' => t('<a href="@url">Create, edit or delete</a> additional CAS usernames associated with this account.', array('@url' => url('user/' . $account->uid . '/cas'))),
1000
          '#element_validate' => array('_cas_name_element_validate'),
1001
          '#weight' => -9,
1002
        );
1003

    
1004
        // See if any additional CAS usernames exist.
1005
        if (!empty($cas_names)) {
1006
          $element['#description'] .= ' <br />' . t('Other CAS usernames: %cas_names.', array('%cas_names' => implode(', ', $cas_names)));
1007
        }
1008
        $form['account']['cas_name'] = $element;
1009
      }
1010
      elseif (cas_is_external_user($account)) {
1011
        // The user is not an administrator, so selectively remove the e-mail
1012
        // and password fields.
1013
        if (variable_get('cas_hide_email', 0)) {
1014
          $form['account']['mail']['#access'] = FALSE;
1015
        }
1016
        if (variable_get('cas_hide_password', 0)) {
1017
          $form['account']['pass']['#access'] = FALSE;
1018
        }
1019
      }
1020
      if (cas_is_external_user($account) && variable_get('cas_hide_password', 0)) {
1021
        // Also remove requirement to validate your current password before
1022
        // changing your e-mail address.
1023
        $form['account']['current_pass']['#access'] = FALSE;
1024
        $form['account']['current_pass_required_values']['#access'] = FALSE;
1025
        $form['account']['current_pass_required_values']['#value'] = array();
1026
        $form['#validate'] = array_diff($form['#validate'], array('user_validate_current_pass'));
1027
      }
1028
      break;
1029

    
1030
    case 'user_pass':
1031
      if (!user_access('administer users') && variable_get('cas_changePasswordURL', '') != '') {
1032
        drupal_goto(variable_get('cas_changePasswordURL', ''));
1033
      }
1034
      break;
1035

    
1036
    case 'user_register_form':
1037
      if (user_access('administer users')) {
1038
        $form['account']['cas_name'] = array(
1039
          '#type' => 'textfield',
1040
          '#title' => t('CAS username'),
1041
          '#default_value' => '',
1042
          '#description' => t('If necessary, additional CAS usernames can be added after the account is created.'),
1043
          '#element_validate' => array('_cas_name_element_validate'),
1044
          '#weight' => -9,
1045
        );
1046
      }
1047
      elseif (variable_get('cas_registerURL', '') != '') {
1048
        drupal_goto(variable_get('cas_registerURL', ''));
1049
      }
1050
      break;
1051

    
1052
    case 'user_admin_account':
1053
      // Insert the CAS username into the second column.
1054
      _cas_array_insert($form['accounts']['#header'], 1, array(
1055
        'cas' => array(
1056
          'data' => 'CAS usernames',
1057
        ),
1058
      ));
1059
      foreach ($form['accounts']['#options'] as $uid => &$row) {
1060
        $cas_usernames = db_query('SELECT cas_name FROM {cas_user} WHERE uid = :uid', array(':uid' => $uid))->fetchCol();
1061
        $row['cas'] = theme('item_list', array('items' => $cas_usernames));
1062
      }
1063
      break;
1064
  }
1065
}
1066

    
1067
/**
1068
 * Form element 'cas_name' validator.
1069
 *
1070
 * If the element is displaying an existing {cas_user} entry, set
1071
 * #cas_user_aid to the corresponding authmap id to avoid spurious
1072
 * validation errors.
1073
 */
1074
function _cas_name_element_validate($element, &$form_state) {
1075
  if (empty($element['#value'])) {
1076
    // Nothing to validate if the name is empty.
1077
    return;
1078
  }
1079

    
1080
  $query = db_select('cas_user')
1081
    ->fields('cas_user', array('uid'))
1082
    ->condition('cas_name', $element['#value']);
1083

    
1084
  // If set, we ignore entries with a specified authmap id. This is used on
1085
  // the user/%user/edit page to not throw validation errors when we do not
1086
  // change the CAS username.
1087
  if (isset($element['#cas_user_aid'])) {
1088
    $query->condition('aid', $element['#cas_user_aid'], '<>');
1089
  }
1090

    
1091
  $uid = $query->execute()->fetchField();
1092

    
1093
  if ($uid !== FALSE) {
1094
    // Another user is using this CAS username.
1095
    form_set_error('cas_name', t('The CAS username is <a href="@edit-user-url">already in use</a> on this site.', array('@edit-user-url' => url('user/' . $uid . '/edit'))));
1096
  }
1097
}
1098

    
1099
/**
1100
 * Login form _validate hook
1101
 */
1102
function cas_login_submit(&$form, &$form_state) {
1103
  if (!empty($form_state['values']['persistent_login'])) {
1104
    $_SESSION['cas_remember'] = 1;
1105
  }
1106
  // Force redirection.
1107
  unset($_GET['destination']);
1108
  drupal_goto('cas', array('query' => $form_state['values']['cas.return_to']));
1109
}
1110

    
1111
function _cas_single_sign_out_check() {
1112
  if (isset($_POST["logoutRequest"])) {
1113
    $cas_logout_request_xml_string = utf8_encode(urldecode($_POST["logoutRequest"]));
1114
    $cas_logout_request_xml = new SimpleXMLElement($cas_logout_request_xml_string);
1115
    if (is_object($cas_logout_request_xml)) {
1116
      $namespaces = $cas_logout_request_xml->getNameSpaces();
1117
      $xsearch = 'SessionIndex';
1118
      if (isset($namespaces['samlp'])) {
1119
        $cas_session_indexes = $cas_logout_request_xml->children($namespaces['samlp'])->SessionIndex;
1120
      }
1121
      else {
1122
        $cas_session_indexes = $cas_logout_request_xml->xpath($xsearch);
1123
      }
1124
      if ($cas_session_indexes) {
1125
        $cas_session_index = (string)$cas_session_indexes[0];
1126
        // Log them out now.
1127
        // first lets find out who we want to log off
1128

    
1129
        $hashed_ticket = hash('sha256', $cas_session_index);
1130
        $record = db_query_range("SELECT cld.uid, u.name FROM {users} u JOIN {cas_login_data} cld ON u.uid = cld.uid WHERE cld.cas_session_id = :ticket", 0, 1, array(':ticket' => $hashed_ticket))->fetchObject();
1131
        if ($record) {
1132
          watchdog('user', 'Session closed for %name by CAS logout request.', array('%name' => $record->name));
1133
          //remove all entry for user id in cas_login_data
1134
          db_delete('cas_login_data')
1135
            ->condition('uid', $record->uid)
1136
            ->execute();
1137

    
1138
          // remove their session
1139
          db_delete('sessions')
1140
            ->condition('uid', $record->uid)
1141
            ->execute();
1142
        }
1143
      }
1144
    }
1145
    // This request is done, so just exit.
1146
    exit();
1147
  }
1148
}
1149

    
1150
/**
1151
 * Return the current CAS username.
1152
 */
1153
function cas_current_user() {
1154
  return isset($_SESSION['cas_name']) ? $_SESSION['cas_name'] : FALSE;
1155
}
1156

    
1157
/**
1158
 * Determine whether the specified user is an "external" CAS user.
1159
 * When settings are set to use drupal as the user repository, then this
1160
 * function will always return true.
1161
 *
1162
 * @param $account
1163
 *   The user object for the user to query. If omitted, the current user is
1164
 *   used.
1165
 *
1166
 * @return
1167
 *   TRUE if the user is logged in via CAS.
1168
 */
1169
function cas_is_external_user($account = NULL) {
1170
  if (!isset($account)) {
1171
    $account = $GLOBALS['user'];
1172
  }
1173
  return in_array(cas_current_user(), $account->cas_names);
1174
}
1175

    
1176

    
1177
function _cas_single_sign_out_save_token($user) {
1178
  // Ok lets save the CAS service ticket to DB so
1179
  // we can handle CAS logoutRequests when they come
1180
  if ($user->uid && $user->uid > 0 && !empty($_SESSION['cas_ticket'])) {
1181
    $hashed_ticket = hash('sha256', $_SESSION['cas_ticket']);
1182
    db_merge('cas_login_data')
1183
      ->key(array('cas_session_id' => $hashed_ticket))
1184
      ->fields(array(
1185
        'cas_session_id' => $hashed_ticket,
1186
        'uid' => $user->uid,
1187
        'created' => time(),
1188
      ))
1189
      ->execute();
1190
    unset($_SESSION['cas_ticket']);
1191
  }
1192
}
1193

    
1194
/**
1195
 * Make sure that we persist ticket because of redirects performed by CAS.
1196
 *
1197
 */
1198
function _cas_single_sign_out_save_ticket() {
1199
  if (isset($_GET['ticket'])) {
1200
    $_SESSION['cas_ticket'] = $_GET['ticket'];
1201
  }
1202
}
1203

    
1204
/**
1205
 * Determine whether a CAS user is blocked.
1206
 *
1207
 * @param $cas_name
1208
 *   The CAS username.
1209
 *
1210
 * @return
1211
 *   Boolean TRUE if the user is blocked, FALSE if the user is active.
1212
 */
1213
function _cas_external_user_is_blocked($cas_name) {
1214
  return db_query("SELECT name FROM {users} u JOIN {cas_user} c ON u.uid = c.uid WHERE u.status = 0 AND c.cas_name = :cas_name", array(':cas_name' => $cas_name))->fetchField();
1215
}
1216

    
1217
/**
1218
 * Invokes hook_cas_user_TYPE() in every module.
1219
 *
1220
 * We cannot use module_invoke() because the arguments need to be passed by
1221
 * reference.
1222
 */
1223
function cas_user_module_invoke($type, &$edit, $account) {
1224
  foreach (module_implements('cas_user_' . $type) as $module) {
1225
    $function = $module . '_cas_user_' . $type;
1226
    $function($edit, $account);
1227
  }
1228
}
1229

    
1230
/**
1231
 * Roles which should be granted to all CAS users.
1232
 *
1233
 * @return
1234
 *   An associative array with the role id as the key and the role name as value.
1235
 */
1236
function cas_roles() {
1237
  $cas_roles = &drupal_static(__FUNCTION__);
1238
  if (!isset($cas_roles)) {
1239
    $cas_roles = array_intersect_key(user_roles(), array_filter(variable_get('cas_auto_assigned_role', array(DRUPAL_AUTHENTICATED_RID => TRUE))));
1240
  }
1241
  return $cas_roles;
1242
}
1243

    
1244
/**
1245
 * Register a CAS user with some default values.
1246
 *
1247
 * @param $cas_name
1248
 *   The name of the CAS user.
1249
 * @param $options
1250
 *   An associative array of options, with the following elements:
1251
 *    - 'edit': An array of fields and values for the new user. If omitted,
1252
 *      reasonable defaults are used.
1253
 *    - 'invoke_cas_user_presave': Defaults to FALSE. Whether or not to invoke
1254
 *      hook_cas_user_presave() on the newly created account.
1255
 *
1256
 * @return
1257
 *   The user object of the created user, or FALSE if the user cannot be
1258
 *   created.
1259
 */
1260
function cas_user_register($cas_name, $options = array()) {
1261
  // Add some reasonable defaults if they have not yet been provided.
1262
  $edit = isset($options['edit']) ? $options['edit'] : array();
1263
  $edit += array(
1264
    'name' => $cas_name,
1265
    'pass' => user_password(),
1266
    'init' => $cas_name,
1267
    'mail' => variable_get('cas_domain', '') ? $cas_name . '@' . variable_get('cas_domain', '') : '',
1268
    'status' => 1,
1269
    'roles' => array(),
1270
  );
1271
  $edit['roles'] += cas_roles();
1272
  $edit['cas_name'] = $cas_name;
1273

    
1274
  // See if the user name is already taken.
1275
  if ((bool) db_select('users')->fields('users', array('name'))->condition('name', db_like($edit['name']), 'LIKE')->range(0, 1)->execute()->fetchField()) {
1276
    return FALSE;
1277
  }
1278

    
1279
  // Create the user account.
1280
  $account = user_save(drupal_anonymous_user(), $edit);
1281
  watchdog("user", 'new user: %n (CAS)', array('%n' => format_username($account)), WATCHDOG_NOTICE, l(t("edit user"), "user/edit/$account->uid"));
1282

    
1283
  if (!empty($options['invoke_cas_user_presave'])) {
1284
    // Populate $edit with some basic properties.
1285
    $edit = array(
1286
      'cas_user' => array(
1287
        'name' => $cas_name,
1288
      ),
1289
    );
1290

    
1291
    // Allow other modules to make their own custom changes.
1292
    cas_user_module_invoke('presave', $edit, $account);
1293

    
1294
    // Clean up extra variables before saving.
1295
    unset($edit['cas_user']);
1296

    
1297
    $account = user_save($account, $edit);
1298
  }
1299

    
1300
  // Reload to ensure that we have a fully populated user object.
1301
  return user_load($account->uid);
1302
}
1303

    
1304
/**
1305
 * Get the CAS attributes of the current CAS user.
1306
 *
1307
 * Ensures that phpCAS is properly initialized before getting the attributes.
1308
 * @see phpCAS::getAttributes()
1309
 *
1310
 * @param $cas_name
1311
 *   If provided, ensure that the currently logged in CAS user matches this
1312
 *   CAS username.
1313
 *
1314
 * @return
1315
 *   An associative array of CAS attributes.
1316
 */
1317
function cas_phpcas_attributes($cas_name = NULL) {
1318
  if (isset($cas_name) && $cas_name != cas_current_user()) {
1319
    // Attributes cannot be extracted for other users, since they are
1320
    // stored in the session variable.
1321
    return array();
1322
  }
1323

    
1324
  cas_phpcas_init();
1325
  if (phpCAS::isAuthenticated()) {
1326
    if (method_exists('phpCAS', 'getAttributes')) {
1327
      return phpCAS::getAttributes();
1328
    }
1329
  }
1330

    
1331
  return array();
1332
}
1333

    
1334

    
1335
/**
1336
 * Insert an array into the specified position of another array.
1337
 *
1338
 * Preserves keys in associative arrays.
1339
 * @see http://www.php.net/manual/en/function.array-splice.php#56794
1340
 */
1341
function _cas_array_insert(&$array, $position, $insert_array) {
1342
  $first_array = array_splice($array, 0, $position);
1343
  $array = array_merge($first_array, $insert_array, $array);
1344
}
1345

    
1346
/**
1347
 * Implements hook_views_api().
1348
 */
1349
function cas_views_api() {
1350
  return array(
1351
    'api' => 3,
1352
    'path' => drupal_get_path('module', 'cas') . '/includes/views',
1353
  );
1354
}
1355

    
1356
/**
1357
 * Redirect a user after they have logged into the website through CAS
1358
 *
1359
 * @param $cas_first_login - TRUE if this is the first time the CAS user
1360
 * logged into the site
1361
 */
1362
function _cas_redirect_after_login($cas_first_login) {
1363
  // When users first log in, we may want to redirect them to a special page if specified
1364
  if ($cas_first_login && variable_get('cas_first_login_destination', '')) {
1365
    $destination = variable_get('cas_first_login_destination', '');
1366
    drupal_goto($destination);
1367
  }
1368
  else {
1369
    // If logged in through forced authentication ('/cas'), then redirect user to the
1370
    // homepage, or to wherever the current "destination" parameter points.
1371
    if (strtolower(current_path()) == 'cas') {
1372
      drupal_goto('');
1373
    }
1374
    // If logged in through gateway feature, then just reload the current path
1375
    // and preserve any query string args that were set
1376
    else {
1377
      drupal_goto(current_path(), array('query' => drupal_get_query_parameters()));
1378
    }
1379
  }
1380
}