Comments

jjesus’s picture

Title: Forgot Password action on user resource » Forgot (Reset) Password action on user resource

I found an old implementation on Services 2.x, though, it doesn't seem to use the form stuff like Services 3.x.

http://drupal.org/node/178051

kylebrowning’s picture

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

Features go against 7.x-3.x dev and are back ported to 6.x

jjesus’s picture

@kyle. Ah, OK, thanks. Do you know offhand if there are plans for Reset (Forgot) Password? Or, do you suggest I fish around in the existing 7.x-3.x stuff to come up with an approach?

jjesus’s picture

Bump.

ygerasimov’s picture

@jjesus there is no such functionality implemented yet. I think we can create additional action reset to reset password for the user. I believe this action should have email as argument.

Would you like to create it?

In order to do that you need to extend user resource definition to add new action and implement callback that will check the passed email and resets the password. Please take a look how core user module does that. I will gladly review your patch.

Thanks!

jjesus’s picture

OK, I have an implementation., but, I added a date of birth check so that no one could get hold of the URL and start resetting everyone's passwords; kinds of begs the question: I don't understand how the security works with Services 3. Seems like anyone can grab the URL and walk all over the site.

But, I'll post the code and someone can help me or just take it and make a better, more general patch.

jjesus’s picture

Add this to the actions array in _user_resource_definition(). The actions array begins with the login action.

        'resetpass' => array(
          'help' => 'Reset password for a user',
          'callback' => '_user_resource_resetpass',
          'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/user_resource'),
          'access callback' => '_user_resource_access',
          'access arguments' => array('resetpass'),
          'access arguments append' => TRUE,
          'args' => array(
            array(
              'name' => 'newpass',
              'type' => 'string',
              'description' => 'The new password.',
              'source' => array('data' => 'newpass'),
              'optional' => FALSE,
            ),
            array(
              'name' => 'email',
              'type' => 'string',
              'optional' => FALSE,
              'source' => array('data' => 'email'),
              'description' => 'email address of user to reset password'
            ),
            array(
              'name' => 'dob_year',
              'type' => 'int',
              'optional' => FALSE,
              'source' => array('data' => 'dob_year'),
              'description' => 'Birthday year of user'
            ),
            array(
              'name' => 'dob_month',
              'type' => 'int',
              'optional' => FALSE,
              'source' => array('data' => 'dob_month'),
              'description' => 'Birthday month of user'
            ),
            array(
              'name' => 'dob_day',
              'type' => 'int',
              'optional' => FALSE,
              'source' => array('data' => 'dob_day'),
              'description' => 'Birthday day of user'
            ),
          ),
        ),

Then, later in the same file add the handler:

function _user_resource_resetpass(
    $newpass, $email, $dob_year, $dob_month, $dob_day) {

  //print("newpass:$newpass email:$email\n");
  //print("$dob_year $dob_month $dob_day\n");

  // first check the email is set and of valid format
  if(user_validate_mail($email))
      return services_error(t('Invalid Email address'), 406); 
  
  // then ensure email is of valid user
  $uid_result = db_fetch_object(db_query("SELECT uid FROM {users} WHERE mail='$email'"));
  if (empty($uid_result)) {
    return services_error(t('Email address does not exist'), 406); 
  }

  $uid = $uid_result->uid;
  $account = user_load($uid);

  // Verify DOB from account
  //
  $profile_dob_string =
    $account->profile_dob['year'] .
    $account->profile_dob['month'] .
    $account->profile_dob['day'];

  $input_dob_string = 
    $dob_year.$dob_month.$dob_day;

  if (strcmp($profile_dob_string, $input_dob_string) != 0) {
    return services_error(t('DOB does not match'), 406); 
  }

  // Hash the password for storage in database
  //
  $hashPassword = md5($newpass);
  db_query("UPDATE {users} SET pass='$hashPassword' WHERE uid='$uid' LIMIT 1");

  $account = user_load($uid);
  return $account;
}
kylebrowning’s picture

Status: Active » Needs work

I would do this the drupal way, submit an email send the email a url, with a token generated, and then have another endpoint that accepts the token.

mrfelton’s picture

Status: Needs work » Needs review
StatusFileSize
new1.92 KB

Here is a patch that adds a new targeted action enabling Drupal's standard password reset functionality to be triggered for specific users. With this, you can make a POST to http://example.com/api/user/1/password_reset to have a standard password reset email sent out to user 1.

mrfelton’s picture

Same thing, without the extra whitespace.

Status: Needs review » Needs work

The last submitted patch, 1303400.10-services-user-password-reset.patch, failed testing.

ygerasimov’s picture

Lets replicate drupal native behavior here. I.e. resetpassword should be action and not targeted action as sometimes users won't know their uid.

Cccess check should be in place. We should not allow to use this actions to anyone but anonymous users.

Also test for this resource should be in place.

@mrfelton will you take care about this issue. I am ready to help.

mrfelton’s picture

@ygerasimov, please could you clarify...

> Lets replicate drupal native behavior here. I.e. resetpassword should be action and not targeted action as sometimes users won't know their uid.

Not sure I follow. How would a password reset email be triggered if you don't know which user it's for?

> Access check should be in place. We should not allow to use this actions to anyone but anonymous users.

I don't really follow this logic. Why only anonymous users? My use case is that an external customer service application needs to be able to trigger password reset emails to be ent out. The external application is generally authenticated, since it needs to be able to do all sorts of things with the user account such as updating profile details, block, activate, etc. If the password reset action is only available to anonymous users then this webservice would need to unauthenticate just to send the email. That doesn't make much sense. The webservice would be far more useful if any user could trigger the password reset email.

mrfelton’s picture

StatusFileSize
new3.5 KB

Updated patch adds an additional resource that allows for resending of the welcome email.Pretty much based on the core patch #5688: Resend welcome message from admin user/edit. I included in this patch for 2 reasons:

1) It's kind of related since its about triggering emails to be sent to users

2) Because I am also using the patch from #10, I was unable to add this functionality without creating a patch conflict/dependency.

You may or may not want to treat this in a separate issue, but here it is for reference anyway.

mrfelton’s picture

StatusFileSize
new3.84 KB

Better logging messages

mrfelton’s picture

Status: Needs work » Needs review
StatusFileSize
new3.84 KB

And without the wiitespace

Status: Needs review » Needs work

The last submitted patch, 1303400.16-services-user-mails.patch, failed testing.

kylebrowning’s picture

Status: Needs work » Needs review
marcingy’s picture

Status: Needs review » Needs work

So core password reset is based off name and email the service should use the exact same approach. Also there is no point in the resend welcome email lets keep this issue focused on the password reset.

mrfelton’s picture

> Also there is no point in the resend welcome email lets keep this issue focused on the password reset.

I agree that that could be tacked in a separate issue, but to say there is no point in the functionality is going a bit far. It's very useful to be able to resend welcome emails. People may not receive them for any number of reasons, and so it is a useful customer service function to be able to resend these emails.

marcingy’s picture

No point may be a bit strong but it should not be a core services feature in my opinion as core offers no way to resend a welcome email.

stongo’s picture

+1 marcingy
I agree this should be a core services feature.

DanielFbrg’s picture

Status: Needs work » Needs review

any working patch here for D6?

marcingy’s picture

Status: Needs review » Needs work

Do not change status please.

stongo’s picture

StatusFileSize
new4.44 KB

This patch is based on comment 16, but adds the ability to use the username instead of the uid as the argument ... When will a user ever know their UID?

Dhammika’s picture

ok, I applied the patch (Post #25), I do see the services module now include two new methods under the user.

Can someone help me by showing me how to use the feature ?

I tried:

http://example.com/?q=api_name/user/18/password_reset

and
http://example.com/?q=api_name/user/joe/password_reset

both did not work.

jerenus’s picture

Status: Needs work » Needs review

Change the status and testing the patch (Post #25).

Status: Needs review » Needs work

The last submitted patch, 1303400.25-services-user-mails.patch, failed testing.

stongo’s picture

Dhammika, did you POST to that url? It should be right.
'REST_PATH/user/USERNAME/password_reset.json' works for me. I also see my patch failed. Maybe I patched it wrong.

Dhammika’s picture

Yes, the POST was the reason I got the error. Thank you.

Now I have a new error:

When I POST :
http://example.com/?q=API_path/user/username/resend_welcome_email.json

I get an error:

["Access denied for user anonymous"]

I assume the user "username" would not be required to be logged or else it would defeat the purpose of the resend_welcome_email.

jibran’s picture

Issue tags: +Needs tests

This needs test as well.

sarath.rajan’s picture

Any update for this issue? Need to reset the user password using service module.. Any default option available? Thanks for all your help..

roam2345’s picture

Added another patch into this one as it needs to live under the "targeted_actions" as well, which allows users to cancel the user. This is a reroll of #16 as no where else in the services user_resource.inc do they user the user name when loading users, hence disregarded change in #25.

issue merged into this one is #2105039: Drupal code provides a way for admins to cancel user accounts.

attached is the patch, as mentioned in both issues still missing tests.

jerenus’s picture

Status: Needs work » Needs review

Bot

roam2345’s picture

Added two new tests to test the password rest and user cancel resources that have been added unfortunately i have no idea how to test the new mail resource. Hopefully this is sufficient to get this patch submitted.

jibran’s picture

Status: Needs review » Needs work

Thanks you @jucallme nice work.

  1. +++ b/tests/functional/ServicesResourceUserTests.test
    @@ -228,6 +228,35 @@ class ServicesResourceUsertests extends ServicesWebtestCase {
    +    $account = $this->drupalCreateUser();
    ...
    +    $account = $this->drupalCreateUser();
    

    Can we add proper permissions to account?

  2. +++ b/tests/functional/ServicesResourceUserTests.test
    @@ -228,6 +228,35 @@ class ServicesResourceUsertests extends ServicesWebtestCase {
    +    $response = $this->servicesPost($this->endpoint->path . '/user/' . $account->uid . '/cancel');
    ...
    +    $response = $this->servicesPost($this->endpoint->path . '/user/' . $account->uid . '/password_reset');
    

    We need assertTrue for this.

  3. +++ b/tests/services.test
    @@ -502,6 +502,17 @@ class ServicesWebTestCase extends DrupalWebTestCase {
    +          'resend_welcome_email' => array(
    

    We need test for this as well. Maybe simple response test with different registration settings

roam2345’s picture

@jibran Here are the changes.

roam2345’s picture

Status: Needs work » Needs review

Bot.

roam2345’s picture

updated patch.

Status: Needs review » Needs work

The last submitted patch, 1303400.39-services-user-mails-and-cancel-users.patch, failed testing.

roam2345’s picture

Status: Needs work » Needs review
StatusFileSize
new10.21 KB

Not sure what was wrong with last patch try again.

kylebrowning’s picture

Awesome.

jibran’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests

Thank you @jucallme for more tests and awesome work. Please create interdiffs it is easier to see new changes. https://drupal.org/node/1488712 http://xjm.drupalgardens.com/blog/interdiffs-how-make-them-and-why-they-...
Overall I think this is complete. RTBC for me.

ygerasimov’s picture

Status: Reviewed & tested by the community » Needs work

Really great work! Thank you a lot!

Some nitpicks.

+++ b/resources/user_resource.incundefined
@@ -468,6 +565,64 @@ function _user_resource_logout_1_1() {
+
+  $user_register = variable_get('user_register', 2);
+  switch ($user_register) {
+    case 0:
+      $op = 'register_admin_created';
+      break;
+    case 1:
+      $op = 'register_no_approval_required';
+      break;
+    case 2:
+      $op = 'register_pending_approval';

Please use constants instead of magic numbers. Like USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL

+++ b/resources/user_resource.incundefined
@@ -535,6 +690,12 @@ function _user_resource_access($op = 'view', $args = array()) {
     case 'delete':
       return user_access('administer users');
+    case 'password_reset':
+      return TRUE;
+    case 'resend_welcome_email':
+      return user_access('administer users');
+    case 'cancel':
+      return user_access('administer users');

Lets group cases. i.e.

case 'password_reset':
  return TRUE;
case 'delete':
case 'resend_welcome_email':
case 'cancel':
  return user_access('administer users');
+++ b/tests/functional/ServicesResourceUserTests.testundefined
@@ -229,6 +229,56 @@ class ServicesResourceUsertests extends ServicesWebtestCase {
+   */
+  function testCancelUser() {

Please add a test that user no 1 cannot be canceled. Security team will be happier.

roam2345’s picture

Here we go all fixed up @ygerasimov

roam2345’s picture

Status: Needs work » Needs review

sorry forgot status.

ygerasimov’s picture

Status: Needs review » Fixed

Great thanks! Committed.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

roam2345’s picture

omar alahmed’s picture

Issue summary: View changes

#25 worked for me and more logical, because forget password should be for the anonymous user by username or email not the uid.

If anyone wanted to load user by email then replace user_load($uid) to user_load_by_mail($uid); $uid is your email, and you should provide your email like this http://example.com/?q=api_name/user/omar@example.com/password_reset

tyler.frankenstein’s picture

Status: Closed (fixed) » Needs review
StatusFileSize
new2.94 KB

It seems we should also be following along with the Drupal core's user_pass() form function here.

If so, we should accept an argument "name" with a value that is equal to a user name or e-mail address. It seems this would also be an Action (similar to login and register), instead of a Targetted Action.

Don't get me wrong, I see the benefit for a resource that accepts a user id, and resets a password. But perhaps that should be a separate resource (and it already is). So...

Here's a patch for a new user resource, request_new_password:

It works by doing a POST to, e.g.:

http://www.example.com/?q=rest/user/request_new_password.json

And sending along a name, e.g.:

name=john

or e-mail address:

name=john@example.com

Before writing any tests for this, I just wanted to make sure this is agreed upon by the Services team first. Please consider using this patch, thanks!

kylebrowning’s picture

Love it, can we get a test and call it done?

kylebrowning’s picture

Status: Needs review » Needs work
kylebrowning’s picture

mradcliffe’s picture

Status: Needs work » Needs review
StatusFileSize
new2.74 KB
new5.68 KB

Added tests. Interdiff attached.

I fixed a small coding standard issue with a stray tab in the test file.

Status: Needs review » Needs work

The last submitted patch, 55: request_new_password-1303400-54.patch, failed testing.

mradcliffe’s picture

Status: Needs work » Needs review
StatusFileSize
new5.87 KB
new2.09 KB

Fix test because drupalGetMails() doesn't provide immediate feedback of e-mails sent. The response code check and a simple count of e-mails should be sufficient.

kylebrowning’s picture

Looks great, haven't tested it yet but will soon.

kylebrowning’s picture

Status: Needs review » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.