Skip to content

Commit 8d91f0d

Browse files
committed
App Passwords: Prevent conflicts when Basic Auth is already used by the site.
Application Passwords uses Basic Authentication to transfer authentication details. If the site is already using Basic Auth, for instance to implement a private staging environment, then the REST API will treat this as an authentication attempt and would end up generating an error for any REST API request. Now, Application Password authentication will only be attempted if Application Passwords is in use by a site. This is flagged by setting an option whenever an Application Password is created. An upgrade routine is added to set this option if any App Passwords already exist. Lastly, creating an Application Password will be prevented if the site appears to already be using Basic Authentication. Props chexwarrior, georgestephanis, adamsilverstein, helen, Clorith, marybaum, TimothyBlynJacobs. Reviewed by TimothyBlynJacobs, helen. Merges [49752] to the 5.6 branch. Fixes #51939. git-svn-id: https://develop.svn.wordpress.org/branches/5.6@49754 602fd350-edb4-49c9-b593-d223f7449a82
1 parent ce25d09 commit 8d91f0d

8 files changed

Lines changed: 110 additions & 21 deletions

File tree

src/wp-admin/authorize-application.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@
8888
);
8989
}
9090

91+
if ( ! empty( $_SERVER['PHP_AUTH_USER'] ) || ! empty( $_SERVER['PHP_AUTH_PW'] ) ) {
92+
wp_die(
93+
__( 'Your website appears to use Basic Authentication, which is not currently compatible with Application Passwords.' ),
94+
__( 'Cannot Authorize Application' ),
95+
array(
96+
'response' => 501,
97+
'link_text' => __( 'Go Back' ),
98+
'link_url' => $reject_url ? add_query_arg( 'error', 'disabled', $reject_url ) : admin_url(),
99+
)
100+
);
101+
}
102+
91103
if ( ! wp_is_application_passwords_available_for_user( $user ) ) {
92104
if ( wp_is_application_passwords_available() ) {
93105
$message = __( 'Application passwords are not available for your account. Please contact the site administrator for assistance.' );

src/wp-admin/includes/upgrade.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,7 @@ function upgrade_all() {
874874
upgrade_550();
875875
}
876876

877-
if ( $wp_current_db_version < 49735 ) {
877+
if ( $wp_current_db_version < 49752 ) {
878878
upgrade_560();
879879
}
880880

@@ -2278,6 +2278,19 @@ function upgrade_560() {
22782278
if ( $wp_current_db_version < 49735 ) {
22792279
delete_transient( 'dirsize_cache' );
22802280
}
2281+
2282+
if ( $wp_current_db_version < 49752 ) {
2283+
$results = $wpdb->get_results(
2284+
$wpdb->prepare(
2285+
"SELECT 1 FROM {$wpdb->usermeta} WHERE meta_key = %s LIMIT 1",
2286+
WP_Application_Passwords::USERMETA_KEY_APPLICATION_PASSWORDS
2287+
)
2288+
);
2289+
2290+
if ( ! empty( $results ) ) {
2291+
update_site_option( WP_Application_Passwords::OPTION_KEY_IN_USE, 1 );
2292+
}
2293+
}
22812294
}
22822295

22832296
/**

src/wp-admin/user-edit.php

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -738,27 +738,34 @@
738738
<?php
739739
}
740740
}
741-
?>
742-
<div class="create-application-password form-wrap">
743-
<div class="form-field">
744-
<label for="new_application_password_name"><?php _e( 'New Application Password Name' ); ?></label>
745-
<input type="text" size="30" id="new_application_password_name" name="new_application_password_name" placeholder="<?php esc_attr_e( 'WordPress App on My Phone' ); ?>" class="input" aria-required="true" aria-describedby="new_application_password_name_desc" />
746-
<p class="description" id="new_application_password_name_desc"><?php _e( 'Required to create an Application Password, but not to update the user.' ); ?></p>
747-
</div>
748741

749-
<?php
750-
/**
751-
* Fires in the create Application Passwords form.
752-
*
753-
* @since 5.6.0
754-
*
755-
* @param WP_User $profileuser The current WP_User object.
756-
*/
757-
do_action( 'wp_create_application_password_form', $profileuser );
758-
?>
742+
if ( empty( $_SERVER['PHP_AUTH_USER'] ) && empty( $_SERVER['PHP_AUTH_PW'] ) ) {
743+
?>
744+
<div class="create-application-password form-wrap">
745+
<div class="form-field">
746+
<label for="new_application_password_name"><?php _e( 'New Application Password Name' ); ?></label>
747+
<input type="text" size="30" id="new_application_password_name" name="new_application_password_name" placeholder="<?php esc_attr_e( 'WordPress App on My Phone' ); ?>" class="input" aria-required="true" aria-describedby="new_application_password_name_desc" />
748+
<p class="description" id="new_application_password_name_desc"><?php _e( 'Required to create an Application Password, but not to update the user.' ); ?></p>
749+
</div>
759750

760-
<?php submit_button( __( 'Add New Application Password' ), 'secondary', 'do_new_application_password' ); ?>
761-
</div>
751+
<?php
752+
/**
753+
* Fires in the create Application Passwords form.
754+
*
755+
* @since 5.6.0
756+
*
757+
* @param WP_User $profileuser The current WP_User object.
758+
*/
759+
do_action( 'wp_create_application_password_form', $profileuser );
760+
?>
761+
762+
<?php submit_button( __( 'Add New Application Password' ), 'secondary', 'do_new_application_password' ); ?>
763+
</div>
764+
<?php } else { ?>
765+
<div class="notice notice-error inline">
766+
<p><?php _e( 'Your website appears to use Basic Authentication, which is not currently compatible with Application Passwords.' ); ?></p>
767+
</div>
768+
<?php } ?>
762769

763770
<div class="application-passwords-list-table-wrapper">
764771
<?php

src/wp-includes/class-wp-application-passwords.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ class WP_Application_Passwords {
2222
*/
2323
const USERMETA_KEY_APPLICATION_PASSWORDS = '_application_passwords';
2424

25+
/**
26+
* The option name used to store whether application passwords is in use.
27+
*
28+
* @since 5.6.0
29+
*
30+
* @type string
31+
*/
32+
const OPTION_KEY_IN_USE = 'using_application_passwords';
33+
2534
/**
2635
* The generated application password length.
2736
*
@@ -31,6 +40,19 @@ class WP_Application_Passwords {
3140
*/
3241
const PW_LENGTH = 24;
3342

43+
/**
44+
* Checks if Application Passwords are being used by the site.
45+
*
46+
* This returns true if at least one App Password has ever been created.
47+
*
48+
* @since 5.6.0
49+
*
50+
* @return bool
51+
*/
52+
public static function is_in_use() {
53+
return (bool) get_site_option( self::OPTION_KEY_IN_USE );
54+
}
55+
3456
/**
3557
* Creates a new application password.
3658
*
@@ -67,6 +89,10 @@ public static function create_new_application_password( $user_id, $args = array(
6789
return new WP_Error( 'db_error', __( 'Could not save application password.' ) );
6890
}
6991

92+
if ( ! get_site_option( self::OPTION_KEY_IN_USE ) ) {
93+
update_site_option( self::OPTION_KEY_IN_USE, true );
94+
}
95+
7096
/**
7197
* Fires when an application password is created.
7298
*

src/wp-includes/user.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ function wp_authenticate_application_password( $input_user, $username, $password
313313
return $input_user;
314314
}
315315

316+
if ( ! WP_Application_Passwords::is_in_use() ) {
317+
return $input_user;
318+
}
319+
316320
$is_api_request = ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) );
317321

318322
/**

src/wp-includes/version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*
2121
* @global int $wp_db_version
2222
*/
23-
$wp_db_version = 49735;
23+
$wp_db_version = 49752;
2424

2525
/**
2626
* Holds the TinyMCE version.

tests/phpunit/tests/auth.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ function setUp() {
3737

3838
$this->user = clone self::$_user;
3939
wp_set_current_user( self::$user_id );
40+
update_site_option( 'using_application_passwords', 1 );
4041
}
4142

4243
public function tearDown() {
@@ -604,4 +605,14 @@ public function test_authenticate_application_password_chunked() {
604605
$this->assertInstanceOf( WP_User::class, $user );
605606
$this->assertSame( self::$user_id, $user->ID );
606607
}
608+
609+
/**
610+
* @ticket 51939
611+
*/
612+
public function test_authenticate_application_password_returns_null_if_not_in_use() {
613+
delete_site_option( 'using_application_passwords' );
614+
615+
$authenticated = wp_authenticate_application_password( null, 'idonotexist', 'password' );
616+
$this->assertNull( $authenticated );
617+
}
607618
}

tests/phpunit/tests/rest-api/rest-application-passwords-controller.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,22 @@ public function test_create_item_invalid_user_id() {
404404
$this->assertErrorResponse( 'rest_user_invalid_id', $response, 404 );
405405
}
406406

407+
/**
408+
* @ticket 51939
409+
*/
410+
public function test_create_item_records_app_passwords_in_use() {
411+
wp_set_current_user( self::$admin );
412+
413+
$this->assertFalse( WP_Application_Passwords::is_in_use() );
414+
415+
$request = new WP_REST_Request( 'POST', '/wp/v2/users/me/application-passwords' );
416+
$request->set_body_params( array( 'name' => 'App' ) );
417+
$response = rest_do_request( $request );
418+
419+
$this->assertSame( 201, $response->get_status() );
420+
$this->assertTrue( WP_Application_Passwords::is_in_use() );
421+
}
422+
407423
/**
408424
* @ticket 42790
409425
*/

0 commit comments

Comments
 (0)