Forum Replies Created

Viewing 15 replies - 1 through 15 (of 395 total)
  • Plugin Author Alexandre Froger

    (@frogerme)

    Hello!

    First of all: THANK YOU for such a detailed scenario – this will help me try to replicate the issue accurately.

    The plugin is updated whenever I have the bandwidth, which lately has not been often. The priority is given to security, then to pull requests fixing bugs, then to bugs, then to optimization.
    The main advantages of the architecture of the plugin is that it does not need updates for every single version of WordPress (which I understand may make it look unmaintained).

    I cannot promise a timeline regarding the issue you described, but I will work on it. Similar sync problems have been reported in the past, with no success of me reproducing the issue, probably because, as you pointed out, it may have had to do with the auto-creation mode upon sync (and not just the update).
    In the meantime, contributions are appreciated. This can be a community effort, but I know it’s not easy either.

    Also, to clarify: the plugin WILL sync all the users upon update. I am under the assumption you are trying to sync only a subset of users based on roles. The role option is to whether sync the role as well as the rest of the user data. If you want to FILTER which users are synced by role, you would have to use one of the hooks provided to hijack the sync process (I believe calling exit() conditionally on a wprus_before_firing_action would do the trick here on the emitter’s end – alternatively, using wprus_before_handle_action_notification and looking at the received data to abort he sync could be a nice fallback guardrail in case unwanted data comes in; the use of both would be ideal, in a custom plugin installed on both your sites).

    Cheers!

    Plugin Author Alexandre Froger

    (@frogerme)

    Hi!
    I have tested adding/removing Package API keys and could not reproduce the reported issue.
    Could you please provide more details, such as detailed steps (with description of what is expected and actual result at each step), or maybe screenshots or a screencast?

    Plugin Author Alexandre Froger

    (@frogerme)

    Hi!
    The intermittent nature of the issue seems to indicate a server configuration source (fail2ban for instance). UpdatePulse Server itself would not give different results if the requests it receives do not change.

    Plugin Author Alexandre Froger

    (@frogerme)

    Hi!

    Glad to read this!

    The cache is an important component of the system though: each file in the folder contains a base64-encoded serialized array with information about the package taken from the zip file, so that it can be served to the clients via the update API without having to parse the zip archive each time. Without the cache, the system will work, but you may see performance degradations (although it very much depends on the number of clients). So it does not seem to be a long term solution.

    What happens if you change the size back to default, clear the cache, and check again?

    Plugin Author Alexandre Froger

    (@frogerme)

    Hello!

    I am not sure how to reproduce the issue ; here are the tests I performed:

    • I am running WordPress latest version on all sites
    • The UPS site serving updates is minimal: UpdatePulse Server plugin is the priority, running a default theme, and other plugins are maintained to a minimum to avoid conflicts
    • I have the Dummy Plugin package installed on a test domain with the version set to 1.4.9, set to use a license
    • I have the Dummy Plugin package at version 1.5 in UPS
    • Visiting the update URL found in the details modal of the package (like https://ups-domain.com/updatepulse-server-update-api/?action=get_metadata&package_id=dummy-plugin&installed_version=1.5.0&checking_for_updates=1&update_type=Plugin) shows the JSON information of the package, with a license_error attribute, itself with code invalid_license (as intended: a license is set)
    • I deactivated the license on the test domain, and re-activated it successfully
    • I checked for updates in wp-admin/update-core.php?force-check=1 successfully
    • I updated the Dummy Plugin package successfully

    Please let me know how I can reproduce the issue.

    Thread Starter Alexandre Froger

    (@frogerme)

    The condition in the code I previously posted is wrong: it would also apply to sub-pages of my-account, which is undesirable.

    Below is my revisted code – there may be better ways to achieve this: relying on the number of query vars seems to be a weak solution.

        public function translate_woocommerce_account_menu_item_classes($classes, $endpoint){
    global $post, $wp;

    if (
    'dashboard' === $endpoint &&
    ( isset( $post ) && wc_get_page_id( 'myaccount' ) === (int) $post->ID ) &&
    ( isset( $wp->query_vars['pagename'] ) && $post->post_name === $wp->query_vars['pagename'] ) &&
    2 === count( $wp->query_vars )
    ) {
    $classes[] = 'is-active';
    }

    return $classes;
    }
    Thread Starter Alexandre Froger

    (@frogerme)

    And here is how to fix it in Falang (relevant extracts for falang/src/Falang/Filter/Site/WooCommerce.php):

    <?php

    namespace Falang\Filter\Site;


    use Automattic\WooCommerce\Internal\Utilities\HtmlSanitizer;

    class WooCommerce {

    /**
    * Constructor
    *
    * @from 1.3.1
    * @update 1.3.28 add woocommerce_product_title filter
    * @update 1.3.44 add filter for widget product list name/title translation
    * @update 1.3.54 add filter for lost password submit redirection woocommerce_get_endpoint_url
    * @update 1.3.62 fix term&condition translation (in the checkout page)
    *
    */
    public function __construct( ) {
    // ...

    // Fix the dashboard "My Account" active class:
    add_filter('woocommerce_account_menu_item_classes', array($this,'translate_woocommerce_account_menu_item_classes'),10,2);
    }

    public function translate_woocommerce_account_menu_item_classes($classes, $endpoint){

    if ( 'dashboard' === $endpoint && ( isset( $wp->query_vars['falang_page'] ) || empty( $wp->query_vars ) ) ) {
    $classes[] = 'is-active';
    }

    return $classes;
    }
    // ...
    }
    Plugin Author Alexandre Froger

    (@frogerme)

    Hello!

    If you are NOT using “Force Login Redirects & Logout Everywhere” in “Miscellaneous” settings, your redirection must wait for site A’s page to load in the browser, because the login is done by the page, not by the server. In that case, you would have to perform your redirection via javascript in your custom plugin/snippet – a server redirection would not work.

    If you ARE using “Force Login Redirects & Logout Everywhere” in “Miscellaneous” settings, I would suggest to investigate the code below and integrate the wprus_action_data filter in your custom plugin/snippet.


    // Code below is taken from Wprus_Api_Abstract:

    // Use this filter.
    apply_filters( 'wprus_action_data', $data, $this->endpoint, $url );

    // in $data, you have a callback_url value that you can edit with the filter.
    $data['callback_url'] = $this->get_redirect_url();

    // this is how the default get_redirect_url method behaves.
    protected function get_redirect_url( $ajax_fallback = false ) {
    $parts = wp_parse_url( home_url() );
    $url = $parts['scheme'] . '://' . $parts['host'] . add_query_arg( null, null );

    if ( false !== strpos( $url, 'admin-ajax.php' ) ) {
    $ajax_fallback = ( $ajax_fallback ) ? $ajax_fallback : home_url();
    $url = apply_filters( 'wprus_get_redirect_url_ajax', $ajax_fallback, $this->endpoint );
    }

    return $url;
    }

    Best of luck!

    Plugin Author Alexandre Froger

    (@frogerme)

    Hi @bernardberry646 !

    This feature is currently not supported.
    However, I will add it in v2.1.3 ; in the meantime, you can replace the content of your wp-content/plugins/wp-remote-users-sync/inc/api/class-wprus-api-logout.php files (on every connected sites) with the following (I tested this, but as it is not officially deployed yet, use at your own risks):

    <?php

    if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly
    }

    class Wprus_Api_Logout extends Wprus_Api_Abstract {

    /*******************************************************************
    * Public methods
    *******************************************************************/

    public function init_notification_hooks() {
    add_action( 'clear_auth_cookie', array( $this, 'notify_remote' ), PHP_INT_MAX - 100, 0 );
    add_action( 'wp_ajax_destroy-sessions', array( $this, 'notify_remote' ), PHP_INT_MIN + 100, 0 );
    }

    public function has_async_actions() {
    return true;
    }

    public function handle_notification() {
    global $is_safari;

    $result = false;
    $proceed = true;
    $data = $this->get_data();

    if ( ! $this->validate( $data ) ) {
    Wprus_Logger::log(
    __( 'Logout action failed - received invalid data.', 'wprus' ),
    'alert',
    'db_log'
    );

    $proceed = false;
    }

    $data = $this->sanitize( $data );
    $site = $this->get_active_site_for_action( $this->endpoint, $data['base_url'] );

    if (
    $proceed &&
    ! get_current_user_id() &&
    (
    self::$browser_support_settings['force_login_logout_strict'] ||
    $is_safari ||
    (
    isset( $_SERVER['HTTP_USER_AGENT'] ) &&
    1 === preg_match( '/iPhone|iPad/', $_SERVER['HTTP_USER_AGENT'] )
    )
    ) &&
    ! self::$browser_support_settings['force_disable_login_logout_strict']
    ) {
    $user = get_user_by( 'login', $data['username'] );

    if ( $user ) {
    wp_set_current_user( $user->ID );
    }
    }

    if ( ! get_current_user_id() ) {
    Wprus_Logger::log(
    __( 'Logout action failed - the user is already logged out.', 'wprus' ),
    'warning',
    'db_log'
    );

    $proceed = false;
    }

    if ( $site && $proceed ) {
    $user = get_user_by( 'login', $data['username'] );

    if ( $user && get_current_user_id() === $user->ID ) {
    $result = true;

    if (
    (
    self::$browser_support_settings['force_login_logout_strict'] ||
    $is_safari ||
    (
    isset( $_SERVER['HTTP_USER_AGENT'] ) &&
    1 === preg_match( '/iPhone|iPad/', $_SERVER['HTTP_USER_AGENT'] )
    )
    ) &&
    ! self::$browser_support_settings['force_disable_login_logout_strict']
    ) {
    wp_destroy_all_sessions();
    } else {
    wp_destroy_current_session();
    wprus_clear_auth_cookie();
    }

    wp_set_current_user( 0 );
    do_action( 'wp_logout', $user->ID );
    Wprus_Logger::log(
    sprintf(
    // translators: %1$s is the username, %2$s is the caller
    __( 'Logout action - successfully logged out user "%1$s" from %2$s.', 'wprus' ),
    $data['username'],
    $site['url']
    ),
    'success',
    'db_log'
    );
    } elseif ( ! $user ) {
    Wprus_Logger::log(
    sprintf(
    // translators: %1$s is the username, %2$s is the caller
    __( 'Logout action aborted - user "%1$s" from %2$s does not exist locally.', 'wprus' ),
    $data['username'],
    $site['url']
    ),
    'warning',
    'db_log'
    );
    } else {
    Wprus_Logger::log(
    sprintf(
    // translators: %1$s is the username, %2$s is the caller
    __( 'Logout action aborted - user "%1$s" from %2$s was not logged in.', 'wprus' ),
    $data['username'],
    $site['url']
    ),
    'warning',
    'db_log'
    );
    }
    } elseif ( ! $site ) {
    Wprus_Logger::log(
    sprintf(
    // translators: %s is the url of the caller
    __( 'Logout action failed - incoming logout action not enabled for %s', 'wprus' ),
    $data['base_url']
    ),
    'alert',
    'db_log'
    );
    }

    return $result;
    }

    public function notify_remote() {

    if ( ! is_user_logged_in() ) {

    return;
    }

    $user = $this->get_user();
    $sites = $this->settings->get_sites( $this->endpoint, 'outgoing' );

    if ( empty( $sites ) ) {
    return;
    }

    $message = $this->use_async() ?
    sprintf(
    // translators: %s is the username
    __( 'Logout action - enqueueing asynchronous actions for username "%s"', 'wprus' ),
    $user->user_login
    ) :
    sprintf(
    // translators: %s is the username
    __( 'Logout action - immediately firing actions for username "%s"', 'wprus' ),
    $user->user_login
    );

    Wprus_Logger::log( $message, 'info', 'db_log' );

    foreach ( $sites as $site ) {

    if ( $this->use_async() ) {
    $this->add_async_action(
    $site['url'],
    array(
    'username' => $user->user_login,
    )
    );

    continue;
    }

    $this->fire_action(
    $site['url'],
    array(
    'username' => $user->user_login,
    )
    );
    }
    }

    /*******************************************************************
    * Protected methods
    *******************************************************************/

    protected function validate( $data ) {
    $valid =
    parent::validate( $data ) &&
    username_exists( $data['username'] );

    return $valid;
    }

    protected function use_async() {
    return doing_action( 'clear_auth_cookie' );
    }

    protected function get_user() {

    if ( doing_action( 'wp_ajax_destroy-sessions' ) ) {
    $user_id = filter_input( INPUT_POST, 'user_id', FILTER_VALIDATE_INT );
    $user = $user_id ? get_userdata( $user_id ) : false;

    if ( $user ) {

    if ( ! current_user_can( 'edit_user', $user->ID ) ) {
    $user = false;
    } elseif ( ! wp_verify_nonce( $_POST['nonce'], 'update-user_' . $user->ID ) ) {
    $user = false;
    }
    }

    if ( ! $user ) {
    Wprus_Logger::log(
    __( 'Logout action failed - user not found, WPRUS connected sites were not notified for logout action.', 'wprus' ),
    'warning',
    'db_log'
    );
    }

    return $user;
    }

    return wp_get_current_user();
    }
    }
    Plugin Author Alexandre Froger

    (@frogerme)

    Hello @andreu and thank you so much for the contribution!

    Your changes have been added to v2.1.2 that was deployed just now – I hope this will solve the issue for the multiple users who have been experiencing password reset troubles.

    Although I have tested hundreds of times (and this is not an exaggeration) with multiple types of setup (same server, actually separate servers on widely different hosting services, sub-folder mode, etc) I have never been able to replicate, let alone understand why it was happening for these users in the first place.

    The distributed nature of the plugin’s logic makes it an order of magnitude harder to troubleshoot compared to classic plugins, and although I cannot really answer your question, your code contribution, your feedback regarding its efficacy, and your insight are particularly valuable!

    Thread Starter Alexandre Froger

    (@frogerme)

    I’ve had similar issues in the plugins I develop – I had to not only do a similar change, but in other cases I had to change the architecture to satisfy the new requirement.
    Above is a solution that only :

    • clears the warning
    • adheres to the WordPress requirements (never calling translation functions before hook init)

    In other words, it is a necessary change, but maybe not the only change necessary 🙂

    Thread Starter Alexandre Froger

    (@frogerme)

    Here is how you can solve it:

    class Falang_Mo extends \MO {

    /**
    * Registers the falang_mo custom post type, only at first object creation
    *
    * @since 1.2
    */
    public function __construct() {
    if ( ! post_type_exists( 'falang_mo' ) ) {
    if ( ! has_action( 'init', array( __CLASS__, 'initialize_post_type' ) ) ) {
    add_action( 'init', array( __CLASS__, 'initialize_post_type' ) );
    }
    }
    }

    public static function initialize_post_type() {
    $labels = array( 'name' => __( 'String translations', 'falang' ) );
    register_post_type( 'falang_mo', array( 'labels' => $labels, 'rewrite' => false, 'query_var' => false, '_falang' => true ) );
    }
    Plugin Author Alexandre Froger

    (@frogerme)

    I have the same error. Could it be because WP is no longer using MD5 to hash passwords??

    if ( ! function_exists( 'wp_hash_password' ) ) {
    function wp_hash_password(
    #[\SensitiveParameter]
    $password
    ) {
    global $wp_hasher;

    if ( version_compare( $GLOBALS['wp_version'], '6.8', '<' ) ) {

    if ( empty( $wp_hasher ) ) {
    require_once ABSPATH . WPINC . '/class-phpass.php';

    $wp_hasher = new PasswordHash( 8, true ); // @codingStandardsIgnoreLine
    }

    do_action( 'wprus_password', $password );

    return $wp_hasher->HashPassword( trim( $password ) );
    }

    if ( ! empty( $wp_hasher ) ) {
    do_action( 'wprus_password', $password );

    return $wp_hasher->HashPassword( trim( $password ) );
    }

    if ( strlen( $password ) > 4096 ) {
    do_action( 'wprus_password', '*' );

    return '*';
    }

    $algorithm = apply_filters( 'wp_hash_password_algorithm', PASSWORD_BCRYPT );
    $options = apply_filters( 'wp_hash_password_options', array(), $algorithm );

    do_action( 'wprus_password', $password );

    if ( PASSWORD_BCRYPT !== $algorithm ) {
    return password_hash( $password, $algorithm, $options );
    }

    $password_to_hash = base64_encode( hash_hmac( 'sha384', trim( $password ), 'wp-sha384', true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode

    return '$wp' . password_hash( $password_to_hash, $algorithm, $options );
    }
    }
    Plugin Author Alexandre Froger

    (@frogerme)

    Thank you for the precision regarding BuddyPress.

    Synchronise all user data: compatible out of the box with WooCommerce, Ultimate Membership, Theme My Login, Gravity Forms, and all user-related plugins as long as they rely on WordPress user metadata and manipulate users with the WordPress user functions.

    Only issues occurring with included plugin features mentioned in “Synchronise all user data”, core WordPress and default WordPress themes (incl. WooCommerce Storefront) will be considered.

    Troubleshooting involving 3rd-party plugins or themes will not be addressed on the WordPress support forum.

    — WP Remote Users Sync plugin description

    Could you please confirm this is a BuddyPress related issue (without BuddyPress, using the default user registration method, without using all caps because this is considered as shouting, and it would be better to keep the discussion civil)?
    The WPRUS Password action relies on overloading the pluggable core function wp_hash_password to introduce a WordPress action that allows synchronisation – however, the code base of BuddyPress does not redefine this function. So the issue is elsewhere.
    If the issue is indeed BuddyPress-related, then it must be coming from the way user creation/update is handled by them (compatibility is guaranteed “as long as [other plugins] […] manipulate users with the WordPress user functions”).

    Plugin Author Alexandre Froger

    (@frogerme)

    Hello,

    I have tested with the following steps (with actions create, update, password, login, logout on both sites):
    – create a user on site A, with [password 0]
    – user is created on site B, with [password 0], and login on site B and site A successfully
    – change password to [password 1] on site B, logout
    – login on site A with the [password 1] successfully
    – change password to [password 2] on site A, logout
    – login with [password 2] on site B successfully
    – delete the user on site A, successfully deleted on site B

    I then changed and tested according to the provided screenshots:
    – site A: create, update, password, login, logout
    – site B: login, logout
    – created a user on site A with [password 0] – and login on site B and site A successfully
    – changed password to [password 1] on site A, logout
    – login with [password 1] on site B successfully

    Please advise how to replicate the issue.

Viewing 15 replies - 1 through 15 (of 395 total)