This is code from user_service.inc, line 162:

  // Any logged in user is by default authenticated,
  // and leaving this role set in the user's roles array
  // causes big problems because of a FAPI hack that controls
  // this checkbox on the user create and edit form (and thus
  // causes problems with drupal_execute()). Therefore we just
  // force it to 0 here.
  if (isset($account['roles'][2])) {
    $account['roles'][2] = 0;
  }

Problem is, I'm using this and getting the following watchdog error when I try to alter an 'authenticated user' array and save it back in to the database:

Illegal choice 0 in Roles element.

So clearly that "fix" breaks things in other cases. It needs a re-think.

For now, unsetting $user['roles'][2] in the client code just prior to calling user.save has the desired effect and bypasses this bug.

CommentFileSizeAuthor
#6 services-827858.patch757 bytesskyredwang
#4 services-827858.patch691 bytesskyredwang

Comments

skyredwang’s picture

Status: Active » Postponed (maintainer needs more info)

Could you explain what are you doing here?

when I try to alter an 'authenticated user' array and save it back in to the database:

greg.harvey’s picture

Sure, basically load a user, change their username and save them again via services. The actual test code looks like this (sorry, this is quite long, but well commented and you can skip most of it - just want you to have the full picture):

/*
 * Now let's retrieve our user to change
 */

// set vars for this connection
$nonce = getUniqueCode("10");
$method_name = 'user.load';
$timestamp = (string) strtotime("now");
$required_args = array();

// now prepare a hash
$hash_parameters = array(
  $timestamp,
  $domain,
  $nonce,
  $method_name,
);

$hash = hash_hmac("sha256", implode(';', $hash_parameters), $kid);

// prepared the arguments for this service
$required_args = array(
  $hash,
  $domain,
  $timestamp,
  $nonce,
  // note, this is now the logged in sessid returned by user.login
  $loggedinsessid,
);


// any user-defined arguments for this method
$user_args = array(
  0 => array('uid' => 55),
);

// add the arguments to the request
foreach ($user_args as $arg) {
  array_push($required_args, $arg);
}

// prepare the request
$request = xmlrpc_encode_request(
  $method_name, $required_args
);

// prepare the request context
$context = stream_context_create(
  array(
    'http' => array(
      'method' => "POST",
      'header' => "Content-Type: text/xml",
      'content' => $request,
    )
  )
);

// connect
$connect = file_get_contents($endpoint, false, $context);
// retrieve the result
$response = xmlrpc_decode($connect);

// display the result on screen
if (xmlrpc_is_fault($response)) {
  print '<h1>Error</h1>';
  print '<pre>'. htmlspecialchars(print_r($response, true)) .'</pre>';  
  trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
  print '<h1>Received</h1>';
  print '<pre>'. htmlentities(print_r($response, true)) .'</pre>';
  // save user for manipulation
  $thisuser = $response;
}


/*
 * Now edit the username of the loaded user
 */

// set the new username
$thisuser['name'] = 'test';
// unset the authenticated user role due to known issue: http://drupal.org/node/827858
unset($thisuser['roles'][2]);

// check the user object before we send it back
print '<h1>User</h1>';
print '<pre>'. htmlspecialchars(print_r($thisuser, true)) .'</pre>';


/*
 * Now save the user array back to Drupal
 */

// set vars for this connection
$nonce = getUniqueCode("10");
$method_name = 'user.save';
$timestamp = (string) strtotime("now");
$required_args = array();

// now prepare a hash
$hash_parameters = array(
  $timestamp,
  $domain,
  $nonce,
  $method_name,
);

$hash = hash_hmac("sha256", implode(';', $hash_parameters), $kid);

// prepared the arguments for this service
$required_args = array(
  $hash,
  $domain,
  $timestamp,
  $nonce,
  // note, this is now the logged in sessid returned by user.login
  $loggedinsessid,
);


// any user-defined arguments for this method
$user_args = array(
  // user array is sent back to Drupal
  0 => $thisuser,
);


// add the arguments to the request
foreach ($user_args as $arg) {
  array_push($required_args, $arg);
}

// prepare the request
$request = xmlrpc_encode_request(
  $method_name, $required_args
);

// prepare the request context
$context = stream_context_create(
  array(
    'http' => array(
      'method' => "POST",
      'header' => "Content-Type: text/xml",
      'content' => $request,
    )
  )
);

// connect
$connect = file_get_contents($endpoint, false, $context);
// retrieve the result
$response = xmlrpc_decode($connect);

// display the result on screen
if (xmlrpc_is_fault($response)) {
  print '<h1>Error</h1>';
  print '<pre>'. htmlspecialchars(print_r($response, true)) .'</pre>';  
  trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
  print '<h1>Received</h1>';
  print '<pre>'. htmlentities(print_r($response, true)) .'</pre>';
  // save user for manipulation
  $thisuser = $response;
}

Without the line commented "// unset the authenticated user role due to known issue: http://drupal.org/node/827858" then I get Illegal choice 0 in Roles element. as a response from the user.save method. With that line, everything works as expected, the altered user object is saved back to Drupal.

greg.harvey’s picture

Status: Postponed (maintainer needs more info) » Active

Changing status, more information provided. =)

skyredwang’s picture

Status: Active » Needs review
StatusFileSize
new691 bytes

this should fix the problem; then you can get rid off your unset line.

Status: Needs review » Needs work

The last submitted patch, services-827858.patch, failed testing.

skyredwang’s picture

Status: Needs work » Needs review
StatusFileSize
new757 bytes

corrected the path

bonvga’s picture

#6: services-827858.patch queued for re-testing.

voxpelli’s picture

Status: Needs review » Needs work

Just a thought - in saving shouldn't roles be an array of role id:s? Considering it's sent into a form field of type "checkboxes" with a couple of options with key role id and value role name?

marcingy’s picture

Version: 6.x-2.2 » 7.x-3.x-dev

This code is still present in 3.x so bumoing to there

kylebrowning’s picture

Status: Needs work » Closed (won't fix)

Unfortunately in 2.x you are going to need to continue to unset the role value, i havnt found a work around for 2.x. (and im not really focusing my time on 2.x)
If youd like to submit a patch

In 3.x however this code should not exist.

I have tested it in 6.x-3.x and half the issue is that you should never be setting the authenticated role.
The same exists for 7.x-3.x.

If you wish to set a role, the data looks like this

$account[roles][RID]=[RID]

RID is always equal to the Role id.

Ive removed this code in 6.x-3.x and 7.x-3.x, but im very reluctant to remove it from 2.x.

At any rate, what I have found is that you should never be setting the authenticated role to a user, is status what you're looking for?