From 73bc6c1d7cfb3bbc82dc8c64807750ea86068c33 Mon Sep 17 00:00:00 2001 From: Bradley M. Froehle Date: Mon, 18 Apr 2011 12:41:09 -0700 Subject: [PATCH] WIP: Implement tokens for the CAS username and attributes. --- cas.module | 45 ++++++++++++++++++++--- cas.test | 43 ++++++++++++++++++++++ cas.tokens.inc | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/cas_test.module | 14 +++++++ 4 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 cas.tokens.inc diff --git a/cas.module b/cas.module index 48f7753..757649b 100644 --- a/cas.module +++ b/cas.module @@ -79,7 +79,7 @@ function cas_login_check() { 'name' => phpCAS::getUser(), 'login' => TRUE, 'register' => variable_get('cas_user_register', TRUE), - 'attributes' => method_exists('phpCAS', 'getAttributes') ? phpCAS::getAttributes() : array(), + 'attributes' => cas_phpcas_attributes(), ); drupal_alter('cas_user', $cas_user); @@ -138,6 +138,9 @@ function cas_login_check() { // final check to make sure we have a good user if ($account && $account->uid > 0) { + // Save the altered CAS name for future use. + $_SESSION['cas_name'] = $cas_name; + $cas_first_login = !$account->login; // Save single sign out information @@ -163,8 +166,6 @@ function cas_login_check() { drupal_set_message(t('You will remain logged in on this computer even after you close your browser.')); } - // Save the altered CAS name for future use. - $_SESSION['cas_name'] = $cas_name; cas_login_page($cas_first_login); } else { @@ -216,9 +217,13 @@ function cas_phpcas_load($path = NULL) { /** * Initialize phpCAS. * - * Must be called after cas_phpcas_load(). + * Will load phpCAS if necessary. */ function cas_phpcas_init() { + if (!defined('PHPCAS_VERSION') || !class_exists('phpCAS')) { + cas_phpcas_load(); + } + $initialized = &drupal_static(__FUNCTION__, FALSE); if ($initialized) { // phpCAS cannot be initialized twice. If you need to force this function @@ -627,7 +632,6 @@ function cas_logout() { global $user; // Build the logout URL. - cas_phpcas_load(); cas_phpcas_init(); $logout_url = phpCAS::getServerLogoutURL(); $options = array(); @@ -1159,6 +1163,37 @@ function cas_user_register($cas_name, $options = array()) { } /** + * Get the CAS attributes of the current CAS user. + * + * Ensures that phpCAS is properly initialized before getting the attributes. + * @see phpCAS::getAttributes(). + * + * @param $cas_name + * If provided, ensure that the currently logged in CAS user matches this + * CAS username. + * + * @return + * An associative array of CAS attributes. + */ +function cas_phpcas_attributes($cas_name = NULL) { + if (isset($cas_name) && $cas_name != cas_current_user()) { + // Attributes cannot be extracted for other users, since they are + // stored in the session variable. + return array(); + } + + cas_phpcas_init(); + if (phpCAS::isAuthenticated()) { + if (method_exists('phpCAS', 'getAttributes')) { + return phpCAS::getAttributes(); + } + } + + return array(); +} + + +/** * Insert an array into the specified position of another array. * * Preserves keys in associative arrays. diff --git a/cas.test b/cas.test index c7f6ecb..3559dfb 100644 --- a/cas.test +++ b/cas.test @@ -684,3 +684,46 @@ class CasRequiredLoginTestCase extends CasTestHelper { $this->assertUrl('node'); } } + +/** + * Test case for CAS attribute tokens. + */ +class CasAttributeTokensTestCase extends CasTestHelper { + + public static function getInfo() { + return array( + 'name' => 'Attribute tokens', + 'description' => 'Test CAS Attribute attribute tokens.', + 'group' => 'Central Authentication Service', + ); + } + + function assertToken($token, $value, $message = '') { + $options = array( + 'query' => array( + 'token' => $token, + 'name' => $this->loggedInUser->cas_name, + ), + ); + $path = 'cas_test/token'; + $out = $this->drupalGet($path, $options); + $this->assertEqual($out, $value, $message, 'Token'); + } + + function testAttributeTokens() { + $account = $this->casCreateUser(); + $attributes = array( + 'surname' => 'Smith', + 'givenName' => 'John', + 'memberOf' => array( + 'CN=Staff,OU=Groups,DC=example,DC=edu', + 'CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu', + ), + ); + $this->casLogin($account, $attributes); + + $this->assertToken('[cas:name]', $account->cas_name); + $this->assertToken('[cas:attribute:surname]', $attributes['surname']); + $this->assertToken('[cas:attribute:memberOf]', $attributes['memberOf'][0]); + } +} diff --git a/cas.tokens.inc b/cas.tokens.inc new file mode 100644 index 0000000..f096a72 --- /dev/null +++ b/cas.tokens.inc @@ -0,0 +1,97 @@ + t('CAS user'), + 'description' => t('Tokens related to a CAS user.'), + 'needs-data' => 'cas', + ); + + $cas['name'] = array( + 'name' => t('Name'), + 'description' => t('The CAS username.'), + ); + $cas['attribute'] = array( + 'name' => t('Attribute'), + 'description' => t('A CAS attribute of the user. (User must be logged in).'), + 'dynamic' => TRUE, + ); + + return array( + 'types' => $types, + 'tokens' => array('cas' => $cas), + ); +} + +/** + * Implements hook_token_info_alter(). + */ +function cas_token_info_alter(&$data) { + $data['tokens']['user']['cas'] = array( + 'name' => t('CAS'), + 'description' => t("The account's primary CAS username."), + 'type' => 'cas', + ); +} + +/** + * Implements hook_tokens(). + */ +function cas_tokens($type, $tokens, array $data = array(), array $options = array()) { + $sanitize = !empty($options['sanitize']); + $replacements = array(); + + if ($type == 'cas' && !empty($data['cas'])) { + $cas = $data['cas']; + foreach ($tokens as $name => $original) { + switch ($name) { + case 'name': + $replacements[$original] = $sanitize ? check_plain($cas) : $cas; + break; + } + } + + // Provide [cas:attribute:?] dynamic tokens. + if ($attribute_tokens = token_find_with_prefix($tokens, 'attribute')) { + $attribute = cas_phpcas_attributes($cas); + foreach ($attribute_tokens as $name => $original) { + if (isset($attribute[$name])) { + $value = $attribute[$name]; + if (is_array($value)) { + $value = $value[0]; + } + $replacements[$original] = $sanitize ? check_plain($value) : $value; + } + } + } + } + + if ($type == 'user' && !empty($data['user'])) { + $account = $data['user']; + foreach ($tokens as $name => $original) { + switch ($name) { + case 'cas': + // Provide [user:cas] token. + if (!empty($account->cas_name)) { + $replacements[$original] = $sanitize ? check_plain($account->cas_name) : $account->cas_name; + } + break; + } + } + + // Provide [user:cas:?] dynamic tokens. + if ($cas_tokens = token_find_with_prefix($tokens, 'cas')) { + $replacements += token_generate('cas', $cas_tokens, array('cas' => $account->cas_name), $options); + } + } + + return $replacements; +} diff --git a/tests/cas_test.module b/tests/cas_test.module index cdfba80..647ef88 100644 --- a/tests/cas_test.module +++ b/tests/cas_test.module @@ -99,6 +99,12 @@ function cas_test_menu() { 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); + $items['cas_test/token'] = array( + 'page callback' => 'cas_test_token_evaluate', + 'title' => 'CAS Token Test', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); return $items; } @@ -271,3 +277,11 @@ function cas_test_single_sign_out($cas_name) { unset($sso[$cas_name]); variable_set('cas_test_sso', $sso); } + +/** + * Evaluate the specified token. + */ +function cas_test_token_evaluate() { + print token_replace($_GET['token'], array('cas' => $_GET['name'])); + exit(0); +} -- 1.7.4.1