Projet

Général

Profil

Paste
Télécharger (41,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / cas / cas.module @ 38c269d5

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
  if (strtolower($item['link_path']) == 'cas' || strtolower($item['link_path']) == 'caslogout') {
451
    $item['options']['alter'] = TRUE;
452
  }
453
}
454

    
455
/**
456
 * Implements hook_translated_menu_item_alter().
457
 *
458
 * Append dynamic query 'destination' to several menu items.
459
 */
460
function cas_translated_menu_link_alter(&$item) {
461
  if (strtolower($item['href']) == 'cas') {
462
    $item['localized_options']['query'] = drupal_get_destination();
463
  }
464
  elseif (strtolower($item['href']) == 'caslogout' && !variable_get('cas_logout_destination', '')) {
465
    $item['localized_options']['query'] = drupal_get_destination();
466
  }
467
}
468

    
469
/**
470
 * Implements hook_user_operations().
471
 */
472
function cas_user_operations($form = array(), $form_state = array()) {
473
  $operations['cas_create'] = array(
474
    'label' => t('Create CAS username'),
475
    'callback' => 'cas_user_operations_create_username',
476
  );
477
  $operations['cas_remove'] = array(
478
    'label' => t('Remove CAS usernames'),
479
    'callback' => 'cas_user_operations_remove_usernames',
480
  );
481
  return $operations;
482
}
483

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

    
511
/**
512
 * Callback function to remove CAS usernames from the account.
513
 *
514
 * @param $uids
515
 *   An array of user ids. For each account, all CAS usernames are removed.
516
 *
517
 * @see cas_user_operations()
518
 */
519
function cas_user_operations_remove_usernames($uids) {
520
  db_delete('cas_user')
521
    ->condition('uid', $uids, 'IN')
522
    ->execute();
523
}
524

    
525
/**
526
 * Implements hook_admin_paths().
527
 */
528
function cas_admin_paths() {
529
  $paths = array(
530
    'user/*/cas' => TRUE,
531
    'user/*/cas/delete/*' => TRUE,
532
  );
533
  return $paths;
534
}
535

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

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

    
574
/**
575
 * Implements hook_user_update().
576
 *
577
 * When a user is updated, change their CAS username if provided.
578
 */
579
function cas_user_update(&$edit, $account, $category) {
580
  if (!array_key_exists('cas_name', $edit)) {
581
    // If the cas_name key is not provided, there is nothing to do.
582
    return;
583
  }
584
  $cas_name = $edit['cas_name'];
585

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

    
625
/**
626
 * Implement hook_user_delete().
627
 *
628
 * When a CAS user is deleted, we need to clean up the entry in {cas_user}.
629
 */
630
function cas_user_delete($account) {
631
  db_delete('cas_user')
632
    ->condition('uid', $account->uid)
633
    ->execute();
634
}
635

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

    
663
  $uid = db_select('cas_user')->fields('cas_user', array('uid'))->condition('cas_name', db_like($cas_name), 'LIKE')->range(0, 1)->execute()->fetchField();
664
  if ($uid) {
665
    return user_load($uid, $reset);
666
  }
667
  return FALSE;
668
}
669

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

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

    
701
  // Build the logout URL.
702
  cas_phpcas_init();
703

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

    
715
  //Make it an absolute url.  This will also convert <front> to the front page.
716
  if ($destination) {
717
    $destination_url = url($destination, array('absolute' => TRUE));
718
    $options = array(
719
      'service' => $destination_url,
720
      'url' => $destination_url,
721
    );
722
  }
723
  else {
724
    $options = array();
725
  }
726

    
727
  // Mimic user_logout().
728
  if ($invoke_hook) {
729
    watchdog('user', 'Session closed for %name.', array('%name' => format_username($user)));
730
    module_invoke_all('user_logout', $user);
731
  }
732

    
733
  // phpCAS automatically calls session_destroy().
734
  phpCAS::logout($options);
735
}
736

    
737
/**
738
 * Implements hook_block_info().
739
 */
740
function cas_block_info() {
741
  $blocks['login']['info'] = t('CAS login');
742
  // Not worth caching.
743
  $blocks['login']['cache'] = DRUPAL_NO_CACHE;
744

    
745
  return $blocks;
746
}
747

    
748
/**
749
 * Implements hook_block_view().
750
 */
751
function cas_block_view($delta = '') {
752
  global $user;
753

    
754
  $block = array();
755

    
756
  switch ($delta) {
757
    case 'login':
758
      // For usability's sake, avoid showing two login forms on one page.
759
      if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
760
        $block['subject'] = t('User login');
761
        $block['content'] = drupal_get_form('cas_login_block');
762
      }
763
      return $block;
764
  }
765
}
766

    
767
/**
768
 * Login form for the CAS login block.
769
 */
770
function cas_login_block($form) {
771
  $form['#action'] = url('cas', array('query' => drupal_get_destination()));
772
  $form['#id'] = 'cas-login-form';
773

    
774
  $form['cas_login_redirection_message'] = array(
775
    '#type' => 'item',
776
    '#markup' => t(variable_get('cas_login_redir_message', CAS_LOGIN_REDIR_MESSAGE)),
777
    '#weight' => -1,
778
  );
779
  $form['actions'] = array('#type' => 'actions');
780
  $form['actions']['submit'] = array(
781
    '#type' => 'submit',
782
    '#value' => t(variable_get('cas_login_invite', CAS_LOGIN_INVITE_DEFAULT)),
783
  );
784

    
785
  return $form;
786
}
787

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

    
804
  if (variable_get('cas_check_frequency', CAS_CHECK_NEVER) == CAS_CHECK_NEVER) {
805
    // The user has disabled the feature.
806
    return FALSE;
807
  }
808

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

    
842
  // Do not force login for XMLRPC, Cron, or Drush.
843
  if (stristr($_SERVER['SCRIPT_FILENAME'], 'xmlrpc.php')) {
844
    return FALSE;
845
  }
846
  if (stristr($_SERVER['SCRIPT_FILENAME'], 'cron.php')) {
847
    return FALSE;
848
  }
849
  if (stristr($_SERVER['SCRIPT_FILENAME'], 'drush')) {
850
    return FALSE;
851
  }
852
  if (!empty($_SERVER['argv'][0]) && stristr($_SERVER['argv'][0], 'drush')) {
853
    return FALSE;
854
  }
855

    
856
  // Test against exclude pages.
857
  if ($pages = variable_get('cas_exclude', CAS_EXCLUDE)) {
858
    $path = drupal_get_path_alias($_GET['q']);
859
    if (drupal_match_path($path, $pages)) {
860
      return FALSE;
861
    }
862
  }
863

    
864
  return TRUE;
865
}
866

    
867
/**
868
 * Determine if we should require the user be authenticated.
869
 *
870
 * @return
871
 *   TRUE if we should require the user be authenticated, FALSE otherwise.
872
 */
873
function _cas_force_login() {
874
  // The 'cas' page is a shortcut to force authentication.
875
  if (strtolower(arg(0)) == 'cas') {
876
    return TRUE;
877
  }
878

    
879
  // Do not process in maintenance mode.
880
  if (variable_get('maintenance_mode', 0)) {
881
    return FALSE;
882
  }
883

    
884
  // Do not force login for XMLRPC, Cron, or Drush.
885
  if (stristr($_SERVER['SCRIPT_FILENAME'], 'xmlrpc.php')) {
886
    return FALSE;
887
  }
888
  if (stristr($_SERVER['SCRIPT_FILENAME'], 'cron.php')) {
889
    return FALSE;
890
  }
891
  if (function_exists('drush_verify_cli') && drush_verify_cli()) {
892
    return FALSE;
893
  }
894

    
895
  // Excluded page do not need login.
896
  if ($pages = variable_get('cas_exclude', CAS_EXCLUDE)) {
897
    $path = drupal_get_path_alias($_GET['q']);
898
    if (drupal_match_path($path, $pages)) {
899
      return FALSE;
900
    }
901
  }
902

    
903
  // Set the default behavior.
904
  $force_login = variable_get('cas_access', 0);
905

    
906
  // If we match the specified paths, reverse the behavior.
907
  if ($pages = variable_get('cas_pages', '')) {
908
    $path = drupal_get_path_alias($_GET['q']);
909
    if (drupal_match_path($path, $pages)) {
910
      $force_login = !$force_login;
911
    }
912
  }
913

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

    
928
  switch ($form_id) {
929
    case 'user_login':
930
    case 'user_login_block':
931
      if (variable_get('cas_login_form', CAS_NO_LINK) != CAS_NO_LINK) {
932
        $form['#attached']['css'][] = drupal_get_path('module', 'cas') . '/cas.css';
933
        $form['#attached']['js'][] = drupal_get_path('module', 'cas') . '/cas.js';
934

    
935
        if (!empty($form_state['input']['cas_identifier'])) {
936
          $form['name']['#required'] = FALSE;
937
          $form['pass']['#required'] = FALSE;
938
          unset($form['#validate']);
939
          $form['#submit'] = array('cas_login_submit');
940
        }
941

    
942
        $items = array();
943
        $items[] = array(
944
          'data' => l(t(variable_get('cas_login_invite', CAS_LOGIN_INVITE_DEFAULT)), '#'),
945
          'class' => array('cas-link'),
946
        );
947

    
948
        $items[] = array(
949
          'data' => l(t(variable_get('cas_login_drupal_invite', CAS_LOGIN_DRUPAL_INVITE_DEFAULT)), '#'),
950
          'class' => array('uncas-link'),
951
        );
952

    
953
        $form['cas_links'] = array(
954
          '#theme' => 'item_list',
955
          '#items' => $items,
956
          '#attributes' => array('class' => array('cas-links')),
957
          '#weight' => 101,
958
        );
959

    
960
        $form['links']['#weight'] = 2;
961

    
962
        $form['cas_login_redirection_message'] = array(
963
          '#type' => 'item',
964
          '#markup' => t(variable_get('cas_login_redir_message', CAS_LOGIN_REDIR_MESSAGE)),
965
          '#weight' => -1,
966
        );
967

    
968
        $form['cas_identifier'] = array(
969
          '#type' => 'checkbox',
970
          '#title' => t(variable_get('cas_login_invite', CAS_LOGIN_INVITE_DEFAULT)),
971
          '#default_value' => variable_get('cas_login_form', CAS_NO_LINK) == CAS_MAKE_DEFAULT,
972
          '#weight' => -1,
973
          '#description' => t(variable_get('cas_login_redir_message', CAS_LOGIN_REDIR_MESSAGE)),
974
        );
975
        $form['cas.return_to'] = array('#type' => 'hidden', '#value' => user_login_destination());
976
      }
977
      break;
978

    
979
    case 'user_profile_form':
980
      $account = $form['#user'];
981
      if (user_access('administer users')) {
982
        // The user is an administrator, so add fields to allow changing the
983
        // CAS username(s) associated with the account.
984
        $cas_names = $account->cas_names;
985
        $aids = array_keys($cas_names);
986

    
987
        $element = array(
988
          '#type' => 'textfield',
989
          '#title' => t('CAS username'),
990
          '#default_value' => array_shift($cas_names),
991
          '#cas_user_aid' => array_shift($aids),
992
          '#description' => t('<a href="@url">Create, edit or delete</a> additional CAS usernames associated with this account.', array('@url' => url('user/' . $account->uid . '/cas'))),
993
          '#element_validate' => array('_cas_name_element_validate'),
994
          '#weight' => -9,
995
        );
996

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

    
1023
    case 'user_pass':
1024
      if (!user_access('administer users') && variable_get('cas_changePasswordURL', '') != '') {
1025
        drupal_goto(variable_get('cas_changePasswordURL', ''));
1026
      }
1027
      break;
1028

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

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

    
1060
/**
1061
 * Form element 'cas_name' validator.
1062
 *
1063
 * If the element is displaying an existing {cas_user} entry, set
1064
 * #cas_user_aid to the corresponding authmap id to avoid spurious
1065
 * validation errors.
1066
 */
1067
function _cas_name_element_validate($element, &$form_state) {
1068
  if (empty($element['#value'])) {
1069
    // Nothing to validate if the name is empty.
1070
    return;
1071
  }
1072

    
1073
  $query = db_select('cas_user')
1074
    ->fields('cas_user', array('uid'))
1075
    ->condition('cas_name', $element['#value']);
1076

    
1077
  // If set, we ignore entries with a specified authmap id. This is used on
1078
  // the user/%user/edit page to not throw validation errors when we do not
1079
  // change the CAS username.
1080
  if (isset($element['#cas_user_aid'])) {
1081
    $query->condition('aid', $element['#cas_user_aid'], '<>');
1082
  }
1083

    
1084
  $uid = $query->execute()->fetchField();
1085

    
1086
  if ($uid !== FALSE) {
1087
    // Another user is using this CAS username.
1088
    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'))));
1089
  }
1090
}
1091

    
1092
/**
1093
 * Login form _validate hook
1094
 */
1095
function cas_login_submit(&$form, &$form_state) {
1096
  if (!empty($form_state['values']['persistent_login'])) {
1097
    $_SESSION['cas_remember'] = 1;
1098
  }
1099
  // Force redirection.
1100
  unset($_GET['destination']);
1101
  drupal_goto('cas', array('query' => $form_state['values']['cas.return_to']));
1102
}
1103

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

    
1122
        $hashed_ticket = hash('sha256', $cas_session_index);
1123
        $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();
1124
        if ($record) {
1125
          watchdog('user', 'Session closed for %name by CAS logout request.', array('%name' => $record->name));
1126
          //remove all entry for user id in cas_login_data
1127
          db_delete('cas_login_data')
1128
            ->condition('uid', $record->uid)
1129
            ->execute();
1130

    
1131
          // remove their session
1132
          db_delete('sessions')
1133
            ->condition('uid', $record->uid)
1134
            ->execute();
1135
        }
1136
      }
1137
    }
1138
    // This request is done, so just exit.
1139
    exit();
1140
  }
1141
}
1142

    
1143
/**
1144
 * Return the current CAS username.
1145
 */
1146
function cas_current_user() {
1147
  return isset($_SESSION['cas_name']) ? $_SESSION['cas_name'] : FALSE;
1148
}
1149

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

    
1169

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

    
1187
/**
1188
 * Make sure that we persist ticket because of redirects performed by CAS.
1189
 *
1190
 */
1191
function _cas_single_sign_out_save_ticket() {
1192
  if (isset($_GET['ticket'])) {
1193
    $_SESSION['cas_ticket'] = $_GET['ticket'];
1194
  }
1195
}
1196

    
1197
/**
1198
 * Determine whether a CAS user is blocked.
1199
 *
1200
 * @param $cas_name
1201
 *   The CAS username.
1202
 *
1203
 * @return
1204
 *   Boolean TRUE if the user is blocked, FALSE if the user is active.
1205
 */
1206
function _cas_external_user_is_blocked($cas_name) {
1207
  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();
1208
}
1209

    
1210
/**
1211
 * Invokes hook_cas_user_TYPE() in every module.
1212
 *
1213
 * We cannot use module_invoke() because the arguments need to be passed by
1214
 * reference.
1215
 */
1216
function cas_user_module_invoke($type, &$edit, $account) {
1217
  foreach (module_implements('cas_user_' . $type) as $module) {
1218
    $function = $module . '_cas_user_' . $type;
1219
    $function($edit, $account);
1220
  }
1221
}
1222

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

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

    
1267
  // See if the user name is already taken.
1268
  if ((bool) db_select('users')->fields('users', array('name'))->condition('name', db_like($edit['name']), 'LIKE')->range(0, 1)->execute()->fetchField()) {
1269
    return FALSE;
1270
  }
1271

    
1272
  // Create the user account.
1273
  $account = user_save(drupal_anonymous_user(), $edit);
1274
  watchdog("user", 'new user: %n (CAS)', array('%n' => format_username($account)), WATCHDOG_NOTICE, l(t("edit user"), "user/edit/$account->uid"));
1275

    
1276
  if (!empty($options['invoke_cas_user_presave'])) {
1277
    // Populate $edit with some basic properties.
1278
    $edit = array(
1279
      'cas_user' => array(
1280
        'name' => $cas_name,
1281
      ),
1282
    );
1283

    
1284
    // Allow other modules to make their own custom changes.
1285
    cas_user_module_invoke('presave', $edit, $account);
1286

    
1287
    // Clean up extra variables before saving.
1288
    unset($edit['cas_user']);
1289

    
1290
    $account = user_save($account, $edit);
1291
  }
1292

    
1293
  // Reload to ensure that we have a fully populated user object.
1294
  return user_load($account->uid);
1295
}
1296

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

    
1317
  cas_phpcas_init();
1318
  if (phpCAS::isAuthenticated()) {
1319
    if (method_exists('phpCAS', 'getAttributes')) {
1320
      return phpCAS::getAttributes();
1321
    }
1322
  }
1323

    
1324
  return array();
1325
}
1326

    
1327

    
1328
/**
1329
 * Insert an array into the specified position of another array.
1330
 *
1331
 * Preserves keys in associative arrays.
1332
 * @see http://www.php.net/manual/en/function.array-splice.php#56794
1333
 */
1334
function _cas_array_insert(&$array, $position, $insert_array) {
1335
  $first_array = array_splice($array, 0, $position);
1336
  $array = array_merge($first_array, $insert_array, $array);
1337
}
1338

    
1339
/**
1340
 * Implements hook_views_api().
1341
 */
1342
function cas_views_api() {
1343
  return array(
1344
    'api' => 3,
1345
    'path' => drupal_get_path('module', 'cas') . '/includes/views',
1346
  );
1347
}
1348

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