cas_attributes ignores the attributes added to the $edit array on user save and asks phpCAS directly for attributes. Because of this, cas_attributes can't be used by admin tasks or cron jobs that add/modify user accounts because cas_attributes will only ever work with user information for the currently authenticated user. As well, modules may make changes to attributes via hook_cas_user_alter(), but cas_attributes won't see these modifications because it asks for the attributes separately rather than working with the altered data.

The attached patch addresses these issues and updates cas_attributes to work with the $edit['attributes'] array that the cas module injects into the $edit array rather than asking phpCAS for the attributes directly. With this change, other modules can implement hook_cas_user_alter() and have their modifications to the attributes available to cas_attributes. As well, other modules can populate $edit['attributes'] with their own data and then call user_save() and have cas_attributes properly populate user fields from the data in the edit array. This allows user-synchronization modules to populate user accounts prior to the users actually logging in via CAS.

Comments

adamfranco’s picture

adamfranco’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, 1: cas_attributes-leaky_abstraction-2187945-0.patch, failed testing.

bkosborne’s picture

Because of this, cas_attributes can't be used by admin tasks or cron jobs that add/modify user accounts because cas_attributes will only ever work with user information for the currently authenticated user

How is this issue in particular solved? The CAS attributes are only populated on a user object when cas_login_check() is called, which calls cas_phpcas_attributes(). From what I can tell, attributes are not persisted to a user account from the CAS module. Any cron/admin task would need to invoke a user login programmatically to obtain the attributes again, right?

adamfranco’s picture

Hi Brian,

Thanks for following up!

Because of this, cas_attributes can't be used by admin tasks or cron jobs that add/modify user accounts because cas_attributes will only ever work with user information for the currently authenticated user

How is this issue in particular solved? The CAS attributes are only populated on a user object when cas_login_check() is called, which calls cas_phpcas_attributes(). From what I can tell, attributes are not persisted to a user account from the CAS module. Any cron/admin task would need to invoke a user login programmatically to obtain the attributes again, right?

I have a custom user-sync module that populates user accounts from a external database and needs to populate the attributes ahead of time so that they are available for reference before the user actually logs in via CAS so that other users can assign them permissions, etc. My external database is the source for the CAS attributes and has the same values available. My cron-job code does the following, setting up the $edit['cas_user']['attributes'] array exactly like the CAS module does:

  ...
  
  // Build the cas_user object and allow modules to alter it.
  $cas_user = array(
    'name' => $webId,
    'login' => TRUE,
    'register' => variable_get('cas_user_register', TRUE),
    'attributes' => $attributes,
  );
  drupal_alter('cas_user', $cas_user);
  
  ...
  
  $account = cas_user_load_by_name($cas_name);
  // Automatic user registration.
  if (!$account && $cas_user['register']) {
    // No account could be found and auto registration is enabled, so attempt
    // to register a new user.
    $account = cas_user_register($cas_name);
    ...
  }
  
  // final check to make sure we have a good user
  if ($account && $account->uid > 0) {
    ...
     // Allow other modules to make their own custom changes.
    cas_user_module_invoke('presave', $edit, $account);
    ...
    if ($changed) {                                                                                                   
      $account = user_save($account, $edit);
    }

The cas_attributes_cas_user_presave(&$edit, $account) hook gets invoked by the cas_user_module_invoke('presave', $edit, $account); line above and this is where cas_attributes' direct usage of phpCAS calls makes for a leaky abstraction. Since there is no user authenticated via phpCAS when cron is running, cas_user_module_invoke('presave', $edit, $account); will fail without this patch, even though the data it needs is available to it in $edit array.

Since cas_attributes_cas_user_presave(&$edit, $account) only needs data that should be in the edit array due to injection by either the CAS module (in the normal login sequence) or by other modules (such as this sync sequence) there is no need for it to fetch the data from elsewhere.

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 1: cas_attributes-leaky_abstraction-2187945-0.patch, failed testing.

yalet’s picture

Version: 7.x-1.0-rc3 » 7.x-1.x-dev

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 1: cas_attributes-leaky_abstraction-2187945-0.patch, failed testing.

yalet’s picture

Well, we have meaningful failures now, so we can work on this. I'm not sure if these fails are due to test logic that needs to be updated to reflect this change, or actual logic failures with the patch. I will look into it.

yalet’s picture

Ok, the tests are failing because the assertToken() method (which is provided by the cas_test module, which is part of the CAS project, and only supports this usage) calls token_replace itself with some values that are given in URL arguments to a special cas_test module path. The test only passes the username, since that was the only piece of information it needed. The tests need to be updated somehow (which, disturbingly, might have to happen in the CAS project queue) to reflect the way this patch wants to work.

yalet’s picture

Status: Needs work » Needs review
StatusFileSize
new5.02 KB

CAS module's token testing is vastly different than Token module's token testing. It would be a lot of work in separate module to modify CAS module's testing for this purpose, so I've opted not to do that. I can't subclass TokenTestHelper, nor can I use its assertTokens method directly (damn you, lack of multiple inheritance!), so I've chosen the method that is terrible but produces the result: copy-pasting Token's assertTokens method into CasAttributesTestHelper.

This patch also rerolls (didn't apply cleanly post- #2190967: Mapping roles from ldap attributes ) and removes an erroneous cas:attribute:cas token from showing up in the cas:attribute:? list.

yalet’s picture

Assigned: adamfranco » Unassigned