Making WordPress.org

Changeset 15346


Ignore:
Timestamp:
10/15/2026 06:11:48 PM (9 days ago)
Author:
obenland
Message:

FreeScout: Ask administrators to disable users rather than delete them, and link usernames to WordPress.org

Closes ​https://github.com/WordPress/wordpress.org/pull/1045.

Location:
sites/trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/api.wordpress.org/public_html/dotorg/freescout/account.php

    r15261 r15346  
    3939 * Whether an account has been blocked from logging in.
    4040 *
    41  * Reports true when that can't be determined, so FreeScout refuses the login.
    42  *
    4341 * @param \WP_User $user Account.
    4442 * @return bool
    4543 */
    4644function is_blocked( \WP_User $user ): bool {
    47         global $nologin_accounts;
    48 
    4945        if ( str_starts_with( $user->user_pass, 'BLOCKED' ) ) {
    50                 return true;
    51         }
    52 
    53         // Accounts wporg-sso refuses a login.
    54         if ( ! empty( $nologin_accounts ) && in_array( $user->user_login, (array) $nologin_accounts, true ) ) {
    5546                return true;
    5647        }
  • sites/trunk/api.wordpress.org/public_html/dotorg/freescout/common.php

    r15261 r15346  
    1919 */
    2020function load_wordpress( string $wp_init_host = '' ): void {
    21         // Set by the config this loads, and read by account.php; without this it would stay local to this function.
    22         global $nologin_accounts;
    23 
    2421        if ( ! $wp_init_host ) {
    2522                $wp_init_host = 'https://api.wordpress.org/';
  • sites/trunk/freescout.wordpress.net/Modules/WPOrgSSO/Http/Middleware/RequireWordPressOrgLogin.php

    r15262 r15346  
    2828 * - Keeps what comes from WordPress.org, and passwords, out of the profile form.
    2929 * - Deletes a mailbox only when its name was typed in to confirm, since users don't know a FreeScout password.
     30 * - Refuses to delete users; they're disabled instead.
    3031 */
    3132final class RequireWordPressOrgLogin {
    … …  
    169170                }
    170171
     172                if ( 'App\Http\Controllers\UsersController@ajax' === $action && 'delete_user' === $request->input( 'action' ) ) {
     173                        // A 200, like core's own errors: the dialog only shows the message of those.
     174                        return response()->json(
     175                                array(
     176                                        'status' => 'error',
     177                                        'msg'    => __( 'Users can’t be deleted. Please disable them instead and remove their mailbox permissions.' ),
     178                                )
     179                        );
     180                }
     181
    171182                if ( 'App\Http\Controllers\MailboxesController@ajax' === $action && 'delete_mailbox' === $request->input( 'action' ) ) {
    172183                        $error = self::check_mailbox_name( $request, $user );
  • sites/trunk/freescout.wordpress.net/Modules/WPOrgSSO/Public/js/users.js

    r15258 r15346  
    33 *
    44 * - "Create a New User" asks for a WordPress.org username; name and email come from the account.
    5  * - The profile doesn't offer to change what WordPress.org keeps up to date, or passwords.
     5 * - The profile doesn't offer to change what WordPress.org keeps up to date, or passwords, or to delete the user.
    66 *
    77 * The server enforces all of this; these only keep the forms from offering it.
    … …  
    4747                        ).remove();
    4848                }
     49
     50                $( '#delete-user-trigger, #delete_user_modal' ).remove();
    4951        } );
    5052
  • sites/trunk/freescout.wordpress.net/Modules/WPOrgSSO/README.md

    r15262 r15346  
    66
    77- **Logging in:** "Log in with WordPress.org" replaces the password form. The account needs two-factor authentication, and blocked accounts can't log in. Accounts are checked again every hour, so a blocked account loses its session too; if WordPress.org can't be reached, the session goes on until the next check.
    8 - **Users:** every FreeScout user is connected to a WordPress.org account. Administrators add users by WordPress.org username, and the name, email, and avatar come from that account. They're updated at every login, and can't be changed in FreeScout. If the WordPress.org email already belongs to another user or a mailbox, the old email stays and a warning is logged.
     8- **Users:** every FreeScout user is connected to a WordPress.org account. Administrators add users by WordPress.org username, and the name, email, and avatar come from that account. They're updated at every login, and can't be changed in FreeScout. On their profile, the username links to their WordPress.org profile. If the WordPress.org email already belongs to another user or a mailbox, the old email stays and a warning is logged.
     9- **Users who leave:** disable them; deleting users is refused. Disabling keeps their mailbox permissions, so remove those by hand.
    910- **Existing users:** administrators connect them on their profile, or with `php artisan wporgsso:connect <email> <wporg-username>`.
    1011- **Passwords:** password logins (except break-glass), resets, and invites are closed. Users have no FreeScout password: new users get none, and logging in with WordPress.org clears the one they had. Only break-glass passwords stay.
  • sites/trunk/freescout.wordpress.net/Modules/WPOrgSSO/Resources/views/edit_user.blade.php

    r15246 r15346  
    44        <div class="col-sm-6">
    55                @if ( $username )
    6                         <p class="form-control-static">{{ $username }}</p>
     6                        <p class="form-control-static"><a href="https://profiles.wordpress.org/{{ rawurlencode( $username ) }}/" target="_blank" rel="noopener">{{ $username }}</a></p>
    77                @elseif ( $can_connect )
    88                        <input id="wporgsso-username" type="text" class="form-control input-sized" name="wporg_username" value="{{ old( 'wporg_username', '' ) }}" maxlength="60" autocomplete="off">
  • sites/trunk/freescout.wordpress.net/Modules/WPOrgSSO/module.json

    r15313 r15346  
    44    "img": "/modules/wporgsso/img/icon.svg",
    55    "description": "Signs agents in with their WordPress.org account, and connects every FreeScout user to one.",
    6     "version": "1.0.2",
     6    "version": "1.0.3",
    77    "detailsUrl": "https://github.com/WordPress/wordpress.org/tree/trunk/freescout.wordpress.net/Modules/WPOrgSSO",
    88    "author": "WordPress.org",
  • sites/trunk/freescout.wordpress.net/Modules/WPOrgSSO/tests/UsersTest.php

    r15258 r15346  
    366366
    367367                $this->create( 'rita' )->assertStatus( 403 );
     368        }
     369
     370        /**
     371         * The profile links the username to the WordPress.org profile.
     372         *
     373         * @return void
     374         */
     375        public function test_profile_links_username_to_wordpress_org_profile(): void {
     376                $user = $this->create_user( User::ROLE_USER );
     377                Account::connect( (int) $user->id, 'rita' );
     378
     379                // The page escapes this, and PHP 8 deprecates escaping null, which tests turn into an exception.
     380                $user->emails = '';
     381                $user->save();
     382
     383                $this->assertStringContainsString(
     384                        '<a href="https://profiles.wordpress.org/rita/" target="_blank" rel="noopener">rita</a>',
     385                        (string) $this->get( route( 'users.profile', array( 'id' => $user->id ) ) )->assertStatus( 200 )->getContent()
     386                );
     387        }
     388
     389        /**
     390         * Users can't be deleted, even by administrators; they're disabled instead.
     391         *
     392         * @return void
     393         */
     394        public function test_users_cannot_be_deleted(): void {
     395                $user = $this->create_user( User::ROLE_USER );
     396
     397                $response = $this->post(
     398                        route( 'users.ajax' ),
     399                        array(
     400                                '_token'  => csrf_token(),
     401                                'action'  => 'delete_user',
     402                                'user_id' => $user->id,
     403                        )
     404                )->assertStatus( 200 );
     405
     406                $this->assertSame( 'error', json_decode( (string) $response->getContent(), true )['status'] );
     407                $this->assertSame( User::STATUS_ACTIVE, (int) $user->refresh()->status );
    368408        }
    369409
Note: See TracChangeset for help on using the changeset viewer.