Skip to:
Content

BuddyPress.org

Changeset 14071


Ignore:
Timestamp:
11/03/2024 06:44:17 PM (21 months ago)
Author:
espellcaste
Message:

Misc changes to the signups and pending accounts.

We are improving how signups and pending accounts are handled in BuddyPress.

  • activation emails resend are blocked for one hour, by default;
  • emails are checked if they are already in use in a signup;
  • signup endpoint (https://developer.buddypress.org/bp-rest-api/reference/signup/) returns a useful error when feature is disabled;
  • Signup::resend: Added the ability to resend to a single ID, instead of an array of IDs.

Props niftythree and imath.

Closes https://github.com/buddypress/buddypress/pull/396
See #9229 and #9145
Fixes #9137

Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bp-core/bp-core-rest-api.php

    r14048 r14071  
    393393}
    394394add_filter( 'rest_pre_dispatch', 'bp_rest_api_v1_dispatch_error', 10, 3 );
     395
     396/**
     397 * Filter the WP REST API response to return a 403 if the signup feature is disabled.
     398 *
     399 * @since 15.0.0
     400 *
     401 * @param mixed           $result Response to replace the requested version with. Can be anything
     402 *                                a normal endpoint can return, or null to not hijack the request.
     403 * @param WP_REST_Server  $server Server instance.
     404 * @param WP_REST_Request $request Request used to generate the response.
     405 *
     406 * @return mixed
     407 */
     408function bp_rest_api_signup_disabled_feature_dispatch_error( $result, $server, $request ) {
     409
     410        // Bail early if the BP REST plugin is active.
     411        if ( bp_rest_is_plugin_active() ) {
     412                return $result;
     413        }
     414
     415        $route = $request->get_route();
     416
     417        if ( empty( $route ) || ! str_contains( $route, 'buddypress/v2/signup' ) ) {
     418                return $result;
     419        }
     420
     421        // Bail early if signups are allowed.
     422        if ( bp_get_signup_allowed() ) {
     423                return $result;
     424        }
     425
     426        return new WP_Error(
     427                'rest_no_route',
     428                __( 'BuddyPress: The user signup feature is currently disabled. Please activate this feature to proceed.', 'buddypress' ),
     429                array( 'status' => 403 )
     430        );
     431}
     432add_filter( 'rest_pre_dispatch', 'bp_rest_api_signup_disabled_feature_dispatch_error', 10, 3 );
  • trunk/src/bp-members/bp-members-functions.php

    r13984 r14071  
    15931593 * Performs the following checks:
    15941594 *   - Is the email address well-formed?
    1595  *   - Is the email address already used?
     1595 *   - Is the email address already used in a user?
     1596 *   - Is the email address already used in a signup?
    15961597 *   - If there are disallowed email domains, is the current domain among them?
    1597  *   - If there's an email domain whitelist, is the current domain on it?
     1598 *   - If there's an email domain allowlist, is the current domain on it?
    15981599 *
    15991600 * @since 1.6.2
     1601 * @since 15.0.0 Check if the email address is already used in a signup.
    16001602 *
    16011603 * @param string $user_email The email being checked.
     
    16301632        }
    16311633
    1632         // Is the email already in use?
     1634        // Is the email already in use in a user?
    16331635        if ( email_exists( $user_email ) ) {
    16341636                $errors['in_use'] = 1;
     1637        }
     1638
     1639        // Is the email already in use in a signup?
     1640        if ( ! isset( $errors['in_use'] ) ) {
     1641                $signups = BP_Signup::get(
     1642                        array( 'user_email' => $user_email )
     1643                );
     1644
     1645                $signup = isset( $signups['signups'] ) && ! empty( $signups['signups'][0] );
     1646
     1647                if ( $signup ) {
     1648                        $errors['in_use'] = 1;
     1649                }
    16351650        }
    16361651
     
    17491764                // Check into signups.
    17501765                $signups = BP_Signup::get(
    1751                         array(
    1752                                 'user_login' => $user_name,
    1753                         )
     1766                        array( 'user_login' => $user_name )
    17541767                );
    17551768
    1756                 $signup = isset( $signups['signups'] ) && ! empty( $signups['signups'][0] ) ? $signups['signups'][0] : false;
     1769                $signup           = isset( $signups['signups'] ) && ! empty( $signups['signups'][0] );
     1770                $user_name_exists = ( empty( $signup ) && username_exists( $user_name ) ) || ! empty( $signup );
    17571771
    17581772                // Check if the username has been used already.
    1759                 if ( username_exists( $user_name ) || ! empty( $signup ) ) {
     1773                if ( true === $user_name_exists ) {
    17601774                        $errors->add( 'user_name', __( 'Sorry, that username already exists!', 'buddypress' ) );
    17611775                }
    17621776
    1763                 // Validate the email address and process the validation results into
    1764                 // error messages.
    1765                 $validate_email = bp_core_validate_email_address( $user_email );
    1766                 bp_core_add_validation_error_messages( $errors, $validate_email );
     1777                // Validate the email address.
     1778                bp_core_add_validation_error_messages(
     1779                        $errors,
     1780                        bp_core_validate_email_address( $user_email )
     1781                );
    17671782
    17681783                // Assemble the return array.
     
    17731788                );
    17741789
    1775                 // Apply WPMU legacy filter.
     1790                /** This filter is documented in wp-includes/ms-functions.php */
    17761791                $result = apply_filters( 'wpmu_validate_user_signup', $result );
    17771792        }
     
    24872502 *
    24882503 * @since 2.0.0
     2504 * @since 15.0.0 Return an error when the activation email resend has been blocked temporarily.
    24892505 *
    24902506 * @global string $error The error message.
     
    25002516
    25012517        // Verify nonce.
    2502         if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'bp-resend-activation' ) ) {
    2503                 die( 'Security check' );
    2504         }
    2505 
    2506         $signup_id = (int) $_GET['id'];
     2518        if ( ! wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), 'bp-resend-activation' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     2519                wp_die( esc_html__( 'There was a problem performing this action. Please try again.', 'buddypress' ) );
     2520        }
     2521
     2522        $signups = BP_Signup::get(
     2523                array( 'include' => (int) $_GET['id'] )
     2524        );
     2525
     2526        // phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
     2527
     2528        if ( empty( $signups['signups'] ) || ! is_array( $signups['signups'] ) || empty( $signups['signups'][0] ) ) {
     2529                $error = __( '<strong>Error</strong>: Invalid signup id.', 'buddypress' );
     2530                return;
     2531        }
     2532
     2533        $signup = $signups['signups'][0];
     2534
     2535        if ( false === BP_Signup::allow_activation_resend( $signup ) ) {
     2536                $error = __( '<strong>Error</strong>: You\'ve reached the limit for resending your account activation email. Please wait a few minutes and try again. If you continue to experience issues, contact support for assistance.', 'buddypress' );
     2537                return;
     2538        }
    25072539
    25082540        // Resend the activation email.
    25092541        // also updates the 'last sent' and '# of emails sent' values.
    2510         $resend = BP_Signup::resend( array( $signup_id ) );
     2542        $resend = BP_Signup::resend( $signup->id );
    25112543
    25122544        // Add feedback message.
     
    25162548                $error = __( 'Activation email resent! Please check your inbox or spam folder.', 'buddypress' );
    25172549        }
     2550
     2551        // phpcs:enable WordPress.WP.GlobalVariablesOverride.Prohibited
    25182552}
    25192553add_action( 'login_form_bp-resend-activation', 'bp_members_login_resend_activation_email' );
  • trunk/src/bp-members/bp-members-template.php

    r14011 r14071  
    30663066
    30673067                /**
    3068                  * Filters whether or not new signups are allowed.
     3068                 * Filters whether new signups are allowed.
    30693069                 *
    30703070                 * @since 1.5.0
  • trunk/src/bp-members/classes/class-bp-members-signup-rest-controller.php

    r14026 r14071  
    765765         */
    766766        public function signup_resend_activation_email( $request ) {
    767                 $signup_id = $request->get_param( 'id' );
    768                 $send      = \BP_Signup::resend( array( $signup_id ) );
     767                $signup = $this->get_signup_object( $request->get_param( 'id' ) );
     768                $send   = $signup::resend( $signup->id );
     769
     770                if ( empty( $send ) ) {
     771                        return new WP_Error(
     772                                'bp_rest_signup_resend_activation_email_fail',
     773                                __( 'There was a problem performing this action. Please try again.', 'buddypress' ),
     774                                array( 'status' => 500 )
     775                        );
     776                }
    769777
    770778                if ( ! empty( $send['errors'] ) ) {
     
    772780                                'bp_rest_signup_resend_activation_email_fail',
    773781                                __( 'Your account has already been activated.', 'buddypress' ),
    774                                 array(
    775                                         'status' => 500,
    776                                 )
     782                                array( 'status' => 500 )
    777783                        );
    778784                }
     
    809815                                'bp_rest_invalid_id',
    810816                                __( 'Invalid signup id.', 'buddypress' ),
    811                                 array(
    812                                         'status' => 404,
    813                                 )
     817                                array( 'status' => 404 )
     818                        );
     819                }
     820
     821                if ( true === $retval && false === BP_Signup::allow_activation_resend( $signup ) ) {
     822                        $retval = new WP_Error(
     823                                'bp_rest_signup_resend_activation_email_fail',
     824                                __( 'You\'ve reached the limit for resending your account activation email. Please wait a few minutes and try again. If you continue to experience issues, contact support for assistance.', 'buddypress' ),
     825                                array( 'status' => 500 )
    814826                        );
    815827                }
     
    976988                $password = (string) $value;
    977989
    978                 if ( empty( $password ) || false !== strpos( $password, '\\' ) ) {
     990                if ( empty( $password ) || str_contains( $password, '\\' ) ) {
    979991                        return new WP_Error(
    980992                                'rest_user_invalid_password',
  • trunk/src/bp-members/classes/class-bp-signup.php

    r13989 r14071  
    44 *
    55 * @package BuddyPress
    6  * @subpackage coreClasses
     6 * @subpackage Signup
    77 * @since 2.0.0
    88 */
     
    160160         * @since 2.0.0
    161161         *
    162          * @param integer $signup_id The ID for the signup being queried.
     162         * @param int $signup_id The ID for the signup being queried.
    163163         */
    164164        public function __construct( $signup_id = 0 ) {
     
    249249                 * was sent in the last day.
    250250                 */
    251                 $this->recently_sent = $this->count_sent && ( $diff < 1 * DAY_IN_SECONDS );
    252 
     251                $this->recently_sent = $this->count_sent && ( $diff < DAY_IN_SECONDS );
    253252        }
    254253
     
    827826         *
    828827         * @since 2.0.0
    829          *
    830          * @param array $signup_ids Single ID or list of IDs to resend.
     828         * @since 15.0.0 Added the ability to resend to a single ID.
     829         *
     830         * @param array|int $signup_ids Single ID or list of IDs to resend.
    831831         * @return array
    832832         */
    833833        public static function resend( $signup_ids = array() ) {
    834                 if ( empty( $signup_ids ) || ! is_array( $signup_ids ) ) {
    835                         return false;
     834                if ( empty( $signup_ids ) ) {
     835                        return array();
     836                }
     837
     838                if ( ! is_array( $signup_ids ) ) {
     839                        $signup_ids = array( $signup_ids );
    836840                }
    837841
    838842                $to_resend = self::get(
    839843                        array(
    840                                 'include' => $signup_ids,
     844                                'include' => wp_parse_id_list( $signup_ids ),
    841845                        )
    842846                );
    843847
    844                 if ( ! $signups = $to_resend['signups'] ) {
    845                         return false;
     848                $signups = $to_resend['signups'];
     849
     850                if ( ! $signups ) {
     851                        return array();
    846852                }
    847853
     
    876882                                $user_id = email_exists( $signup->user_email );
    877883
    878                                 if ( ! empty( $user_id ) && 2 != self::check_user_status( $user_id ) ) {
     884                                if ( ! empty( $user_id ) && 2 !== self::check_user_status( $user_id ) ) {
    879885
    880886                                        // Status is not 2, so user's account has been activated.
     
    886892                                        continue;
    887893
    888                                 // Send the validation email.
     894                                        // Send the validation email.
    889895                                } else {
    890896                                        $salutation = $signup->user_login;
     
    907913
    908914                /**
    909                  * Fires after activation emails are resent.
     915                 * Fires after activation email(s) are/is resent.
    910916                 *
    911917                 * @since 2.0.0
     
    924930                 */
    925931                return apply_filters( 'bp_core_signup_resend', $result );
     932        }
     933
     934        /**
     935         * Check if an activation email can be resent.
     936         *
     937         * @since 15.0.0
     938         *
     939         * @param BP_Signup $signup The signup object.
     940         * @return bool
     941         */
     942        public static function allow_activation_resend( $signup ) {
     943
     944                // Bail if the signup is not a BP_Signup object.
     945                if ( ! $signup instanceof BP_Signup ) {
     946                        return false;
     947                }
     948
     949                // Allow the activation email to be sent if not already.
     950                if ( ! $signup->recently_sent || ! $signup->count_sent ) {
     951                        return true;
     952                }
     953
     954                $sent_at = mysql2date( 'U', $signup->date_sent );
     955                $now     = time();
     956                $diff    = $now - $sent_at;
     957
     958                /**
     959                 * Filters the lock time for the resend activation.
     960                 *
     961                 * @since 15.0.0
     962                 *
     963                 * @param float|int $lock_time The lock time for the resend activation. Default: 1 hour.
     964                 * @param BP_Signup $signup The signup object.
     965                 */
     966                $lock_time = apply_filters( 'bp_core_signup_resend_activation_lock_time', HOUR_IN_SECONDS, $signup );
     967
     968                // If the activation email was sent less than the lock time ago.
     969                return false === ( $diff < $lock_time );
    926970        }
    927971
  • trunk/tests/phpunit/testcases/members/class-bp-signup.php

    r14026 r14071  
    5151                );
    5252
    53                 $signup = BP_Signup::add( $args );
     53                $signup = self::factory()->signup->create( $args );
    5454                $this->assertNotEmpty( $signup );
    5555
     
    7070
    7171                // Add new signup without a custom field visibility set for field_1.
    72                 $signup = BP_Signup::add( array(
     72                $signup = self::factory()->signup->create( array(
    7373                        'title' => 'Foo bar',
    7474                        'user_login' => 'user1',
     
    484484                        ),
    485485                );
    486                 $s1 = BP_Signup::add( $args );
     486                $s1 = self::factory()->signup->create( $args );
    487487
    488488                $args['meta']['field_1'] = 'Fozz';
    489                 $s2 = BP_Signup::add( $args );
     489                $s2 = self::factory()->signup->create( $args );
    490490
    491491                // Should find both.
     
    533533                        ),
    534534                );
    535                 $s1 = BP_Signup::add( $args );
     535                $s1 = self::factory()->signup->create( $args );
    536536
    537537                $args['meta']['field_1'] = 'Fozz';
    538                 $s2 = BP_Signup::add( $args );
     538                $s2 = self::factory()->signup->create( $args );
    539539
    540540                // Should find both.
     
    637637        }
    638638
     639        /**
     640         * @group resend
     641         */
    639642        public function test_bp_core_signup_send_validation_email_should_increment_sent_count() {
    640643                $activation_key = wp_generate_password( 32, false );
    641644                $user_email     = 'accountone@example.com';
    642                 $s1             = self::factory()->signup->create( array(
     645                $s1             = self::factory()->signup->create_and_get( array(
    643646                        'user_login'     => 'accountone',
    644647                        'user_email'     => $user_email,
     
    646649                ) );
    647650
    648                 $signup = new BP_Signup( $s1 );
    649                 $this->assertEquals( 0, $signup->count_sent );
     651                $this->assertEquals( 0, $s1->count_sent );
    650652
    651653                bp_core_signup_send_validation_email( 0, $user_email, $activation_key );
    652654
    653                 $signup = new BP_Signup( $s1 );
     655                $signup = new BP_Signup( $s1->id );
    654656                $this->assertEquals( 1, $signup->count_sent );
    655657        }
     658
     659        /**
     660         * @ticket BP9137
     661         * @group resend
     662         */
     663        public function test_bp_core_signup_resend_email_activation() {
     664                $s1 = self::factory()->signup->create_and_get(
     665                        array(
     666                                'user_login'     => 'user' . wp_rand( 1, 20 ),
     667                                'user_email'     => sprintf( 'user%d@example.com', wp_rand( 1, 20 ) ),
     668                                'registered'     => bp_core_current_time(),
     669                                'activation_key' => wp_generate_password( 32, false ),
     670                                'meta'           => array(
     671                                        'field_1' => 'Foo Bar',
     672                                ),
     673                        )
     674                );
     675
     676                BP_Signup::resend( $s1->id );
     677
     678                $this->assertFalse( BP_Signup::allow_activation_resend( 0 ) );
     679                $this->assertFalse( BP_Signup::allow_activation_resend( '' ) );
     680                $this->assertTrue( BP_Signup::allow_activation_resend( $s1 ) );
     681
     682                $s1->count_sent = 0;
     683                $this->assertTrue( BP_Signup::allow_activation_resend( $s1 ) );
     684
     685                $s1->count_sent = 1;
     686                $s1->recently_sent = true;
     687                $this->assertFalse( BP_Signup::allow_activation_resend( $s1 ) );
     688
     689                add_filter( 'bp_core_signup_resend_activation_lock_time', '__return_zero' );
     690                $this->assertTrue( BP_Signup::allow_activation_resend( $s1 ) );
     691                remove_filter( 'bp_core_signup_resend_activation_lock_time', '__return_zero' );
     692        }
    656693}
  • trunk/tests/phpunit/testcases/members/functions.php

    r14026 r14071  
    848848
    849849        /**
    850          * Provider for the test_bp_core_validate_user_signup() test.
     850         * Provider for the test_bp_core_validate_user_signup_errors() test.
    851851         *
    852852         * @return array[]
  • trunk/tests/phpunit/testcases/members/test-signup-controller.php

    r14070 r14071  
    210210         * @group create_item
    211211         */
     212        public function test_creating_multiple_pending_accounts_with_different_usernames() {
     213                $request = new WP_REST_Request( 'POST', $this->endpoint_url );
     214
     215                $params = $this->set_signup_data( array( 'user_login' => 'user1' ) );
     216                $request->set_body_params( $params );
     217                $request->set_param( 'context', 'edit' );
     218                $response = $this->server->dispatch( $request );
     219
     220                $this->assertEquals( 200, $response->get_status() );
     221
     222                $signup = $response->get_data();
     223
     224                $this->assertSame( $signup['user_login'], $params['user_login'] );
     225                $this->assertSame( $signup['user_email'], $params['user_email'] );
     226                $this->assertTrue( ! isset( $signup['activation_key'] ) );
     227
     228                // Test with the same email.
     229                $params = $this->set_signup_data( array( 'user_login' => 'user2' ) );
     230                $request->set_body_params( $params );
     231                $request->set_param( 'context', 'edit' );
     232                $response = $this->server->dispatch( $request );
     233
     234                $this->assertErrorResponse( 'bp_rest_signup_validation_failed', $response, 500, 'This user\'s email is already registered.' );
     235
     236                // Test with a different email.
     237                $params = $this->set_signup_data( array( 'user_login' => 'user2', 'user_email' => 'user2@example.com' ) );
     238                $request->set_body_params( $params );
     239                $request->set_param( 'context', 'edit' );
     240                $response = $this->server->dispatch( $request );
     241
     242                $this->assertEquals( 200, $response->get_status() );
     243        }
     244
     245        /**
     246         * @group create_item
     247         */
    212248        public function test_create_item_with_signup_fields() {
    213249                $g1 = $this->bp::factory()->xprofile_group->create();
     
    664700         * @group resend_item
    665701         */
    666         public function test_resend_acivation_email_to_active_signup() {
     702        public function test_resend_activation_email_to_active_signup() {
    667703                $signup_id = $this->create_signup();
    668704                $signup    = new BP_Signup( $signup_id );
     
    689725         * @group resend_item
    690726         */
     727        public function test_resend_activation_email_to_locked_signup() {
     728                $signup_id = $this->create_signup();
     729
     730                BP_Signup::resend( $signup_id );
     731
     732                $request = new WP_REST_Request( 'PUT', $this->endpoint_url . '/resend' );
     733                $request->set_param( 'id', $signup_id );
     734                $request->set_param( 'context', 'edit' );
     735                $response = $this->server->dispatch( $request );
     736
     737                $this->assertEquals( 500, $response->get_status() );
     738
     739                $error_code = 'bp_rest_signup_resend_activation_email_fail';
     740                $error      = $response->as_error();
     741                $message    = $error->get_error_message( $error_code );
     742
     743                $this->assertErrorResponse( $error_code, $response, 500 );
     744                $this->assertSame(
     745                        $message,
     746                        "You've reached the limit for resending your account activation email. Please wait a few minutes and try again. If you continue to experience issues, contact support for assistance."
     747                );
     748        }
     749
     750        /**
     751         * @group resend_item
     752         */
     753        public function test_resend_activation_email_to_locked_signup_with_hook() {
     754                $signup_id = $this->create_signup();
     755
     756                BP_Signup::resend( $signup_id );
     757
     758                add_filter( 'bp_core_signup_resend_activation_lock_time', '__return_zero' );
     759
     760                $request = new WP_REST_Request( 'PUT', $this->endpoint_url . '/resend' );
     761                $request->set_param( 'id', $signup_id );
     762                $request->set_param( 'context', 'edit' );
     763                $response = $this->server->dispatch( $request );
     764
     765                $this->assertEquals( 200, $response->get_status() );
     766
     767                $all_data = $response->get_data();
     768
     769                $this->assertTrue( $all_data['sent'] );
     770
     771                remove_filter( 'bp_core_signup_resend_activation_lock_time', '__return_zero' );
     772        }
     773
     774        /**
     775         * @group resend_item
     776         */
    691777        public function test_resend_activation_email_invalid_signup_id() {
    692778                $request = new WP_REST_Request( 'PUT', $this->endpoint_url . '/resend' );
     
    734820
    735821        protected function create_signup() {
    736                 return BP_Signup::add(
     822                return $this->bp::factory()->signup->create(
    737823                        array(
    738824                                'user_login'     => 'user' . wp_rand( 1, 20 ),
     
    805891                $this->assertEquals( array( 'view', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
    806892        }
     893
     894        public function test_bp_rest_api_signup_disabled_feature_dispatch_error() {
     895                // Disable signups registration.
     896                bp_update_option( 'users_can_register', 0 );
     897
     898                if  ( is_multisite() ) {
     899                        update_site_option( 'registration', '' );
     900                }
     901
     902                $request  = new WP_REST_Request( 'OPTIONS', $this->endpoint_url );
     903                $response = $this->server->dispatch( $request );
     904                $data     = $response->get_data();
     905
     906                $this->assertEquals( 403, $response->get_status() );
     907                $this->assertSame(
     908                        $data['message'],
     909                        'BuddyPress: The user signup feature is currently disabled. Please activate this feature to proceed.'
     910                );
     911        }
    807912}
Note: See TracChangeset for help on using the changeset viewer.