froger-me / wp-remote-users-sync

Synchronise users across WordPress websites
GNU General Public License v3.0
71 stars 34 forks source link

Passwords not syncing when creating a user in the dashboard #28

Open igor-mitic opened 3 years ago

igor-mitic commented 3 years ago

Issue:

When creating a new user, or using the profile.php form to update a users' password, the password will not change on the linked site.

Tested on a blank WordPress 5.6.2 install (also with 5.5.1), with only WP Remote Users Sync active and a default theme active.

The user is properly created, and all of the user meta is sent - but only the password field does not carry over.

The logs return the following:

Array
(
    [data] => Array
        (
            [user_login] => eljefe
            [user_nicename] => eljefe
            [user_url] => 
            [user_email] => i787@plume.co.uk
            [display_name] => eljefe
            [nickname] => eljefe
            [first_name] => 
            [last_name] => 
            [description] => 
            [rich_editing] => true
            [syntax_highlighting] => true
            [comment_shortcuts] => false
            [admin_color] => fresh
            [use_ssl] => 0
            [user_registered] => 2021-03-03 15:22:09
            [show_admin_bar_front] => true
            [locale] => 
            [user_pass] => ** HIDDEN **
            [roles] => Array
                (
                    [set] => 
                    [add] => 
                )

        )

)

Other forms of password resetting seem to work fine.

igor-mitic commented 3 years ago

Going into the plugin and printing out the password for dev purposes returns a completely different pass - one that is auto-generated instead of the one set on the triggering site.

Edit: The same also happens if a user is generated through the WooCommerce checkout page and a password is sent to them through the shopping site.

igor-mitic commented 3 years ago

I don't understand the plugin very well, so I might be wrong - but you may find this useful.

I think that the password is not being carried over at all from class-wprus-api-create.php to handle_notify_remote_data() in class-wprus-api-password.php. It's never truly grabbed from the user at any stage of the creation process (and even if it were grabbed by getting $user->user_pass, it might just get encoded again in L30 in class-wprus-api-password.php, since $user->user_pass returns the MD5 encoded password already).

It also doesn't help that PHP_INT_MAX is used when hooking into user_register in the plugin since there's no way to overwrite the process without editing the plugin directly.

I think updating the user_pass field in the DB directly with the encoded version grabbed from $user->user_pass might be the way forward though there may be downsides to this approach.

froger-me commented 3 years ago

Hi @igor-mitic ! Thanks for digging into the code!

$data['user_pass'] contains the password when the Password action is enable (for both Create and Update) in clear. I just tested again (both Create and Update), successfully. I even added the output of the password to test with error_log between line 27 and 28 of class-wprus-api-password.php, like you tested, and did so successfully.

Because the password is in clear after decryption of the payload, it has to go through wp_hash_password to be stored in the database - it cannot be updated directly in the DB.

Yes, there is a way to bypass the process, if you need: overwriting the API classes of your choice using the wprus_api filter. But I think your issue is elsewhere, I just cannot reproduce it.

igor-mitic commented 3 years ago

Hi @froger-me - thank you for your reply.

Just so I'm sure I'm not losing my mind on this - I've tested this on a blank install with no other plugins active - the password does get sent over, the issue is it's a different password. If you haven't, can you please try changing a password on Site A in /wp-admin/profile.php and then trying to log in with that password on Site B?

What happens on my end is that the password goes get sent over, just that it's intercepted and re-generated after the profile update so it's always different between Site A and Site B, if that makes sense.

The same happens when you create an account through WooCommerce, for example on checkout. The password generated by Woo on Site A doesn't match the one on Site B.

If you tried the above I think I'll need to start looking into the php version and server setup we have (or use wprus_api like you suggested) - I'm completely lost on what else it could be.

froger-me commented 3 years ago

Hi @igor-mitic ,

If you haven't, can you please try changing a password on Site A in /wp-admin/profile.php and then trying to log in with that password on Site B?

I have, with success. I just went ahead and even did another series of tests with 2 different security keys & salt values in wp-config.php (from https://api.wordpress.org/secret-key/1.1/salt/). No issue reproduced.

just that it's intercepted and re-generated after the profile update so it's always different between Site A and Site B, if that makes sense.

I mean, I understand the sentence, it's just what's not happening in code if the actions are configured properly:

public function handle_notification_password_data( $data, $site, $user = false ) {

// THIS IS EXECUTED - no password regenerated ; only hashed to be used when updating the user to emulate wp_update_user() ; plaintext when creating because wp_insert_user() takes care of hashing in this case only
        if ( $user ) {
            if ( $data['user_pass'] ) {
                $data['user_pass'] = wp_hash_password( $data['user_pass'] );
            }
        }
// THIS IS *NOT* EXECUTED because we have the action "Password" active for incoming site, and we have a 'user_pass' value in the payload
        if ( ! $site['incoming_actions']['password'] || ! $data['user_pass'] ) {

            if ( $user ) {
                unset( $data['user_pass'] );
            } else {
                $data['user_pass'] = wp_generate_password( 16 );
            }
        }

        return $data;
    }
thedavidthomas commented 2 years ago

I have manually copied and pasted the data between the sites user_pass column in the databases as this is the only way I could get the sync to work and retain existing passwords.

if users already exist in Site A before setting up an additional remote site (Site B) unless you "set a new password" on the user edit screen the password from Site A is not passed to the user created or updatedin Site B even with Password selected as an Outgoing action on Site A and an Incoming action on Site B.

hurricane-socrates commented 2 months ago

Yeah, I agree. Passwords are not syncing. I now have to go though a great deal of work to handle password updates.