Changeset 49109
- Timestamp:
- 10/08/2020 10:12:02 PM (4 years ago)
- Location:
- trunk
- Files:
-
- 8 added
- 17 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Gruntfile.js
r49101 r49109 239 239 files: { 240 240 [ WORKING_DIR + 'wp-admin/js/accordion.js' ]: [ './src/js/_enqueues/lib/accordion.js' ], 241 [ WORKING_DIR + 'wp-admin/js/application-passwords.js' ]: [ './src/js/_enqueues/admin/application-passwords.js' ], 242 [ WORKING_DIR + 'wp-admin/js/auth-app.js' ]: [ './src/js/_enqueues/admin/auth-app.js' ], 241 243 [ WORKING_DIR + 'wp-admin/js/code-editor.js' ]: [ './src/js/_enqueues/wp/code-editor.js' ], 242 244 [ WORKING_DIR + 'wp-admin/js/color-picker.js' ]: [ './src/js/_enqueues/lib/color-picker.js' ], … … 797 799 file_mappings: { 798 800 'src/wp-admin/js/accordion.js': 'src/js/_enqueues/lib/accordion.js', 801 'src/wp-admin/js/application-passwords.js': 'src/js/_enqueues/admin/application-passwords.js', 802 'src/wp-admin/js/auth-app.js': 'src/js/_enqueues/admin/auth-app.js', 799 803 'src/wp-admin/js/code-editor.js': 'src/js/_enqueues/wp/code-editor.js', 800 804 'src/wp-admin/js/color-picker.js': 'src/js/_enqueues/lib/color-picker.js', -
trunk/src/wp-admin/css/forms.css
r48419 r49109 757 757 } 758 758 759 .form-table td fieldset p label { 759 .form-table td fieldset p label { 760 760 margin-top: 0 !important; 761 761 } … … 840 840 .color-option { 841 841 cursor: pointer; 842 } 843 844 .new-application-password-notice.notice { 845 margin-top: 20px; 846 margin-bottom: 0; 842 847 } 843 848 -
trunk/src/wp-admin/includes/list-table.php
r48574 r49109 34 34 'WP_Theme_Install_List_Table' => array( 'themes', 'theme-install' ), 35 35 'WP_Plugins_List_Table' => 'plugins', 36 'WP_Application_Passwords_List_Table' => 'application-passwords', 36 37 37 38 // Network Admin. -
trunk/src/wp-admin/includes/user.php
r48313 r49109 595 595 ); 596 596 } 597 598 /** 599 * Checks if the Authorize Application Password request is valid. 600 * 601 * @since 5.6.0 602 * 603 * @param array $request { 604 * The array of request data. All arguments are optional and may be empty. 605 * 606 * @type string $app_name The suggested name of the application. 607 * @type string $success_url The url the user will be redirected to after approving the application. 608 * @type string $reject_url The url the user will be redirected to after rejecting the application. 609 * } 610 * @param WP_User $user The user authorizing the application. 611 * @return true|WP_Error True if the request is valid, a WP_Error object contains errors if not. 612 */ 613 function wp_is_authorize_application_password_request_valid( $request, $user ) { 614 $error = new WP_Error(); 615 616 if ( ! empty( $request['success_url'] ) ) { 617 $scheme = wp_parse_url( $request['success_url'], PHP_URL_SCHEME ); 618 619 if ( 'http' === $scheme ) { 620 $error->add( 621 'invalid_redirect_scheme', 622 __( 'The success url must be served over a secure connection.' ) 623 ); 624 } 625 } 626 627 if ( ! empty( $request['reject_url'] ) ) { 628 $scheme = wp_parse_url( $request['reject_url'], PHP_URL_SCHEME ); 629 630 if ( 'http' === $scheme ) { 631 $error->add( 632 'invalid_redirect_scheme', 633 __( 'The rejection url must be served over a secure connection.' ) 634 ); 635 } 636 } 637 638 /** 639 * Fires before application password errors are returned. 640 * 641 * @since 5.6.0 642 * 643 * @param WP_Error $error The error object. 644 * @param array $request The array of request data. 645 * @param WP_User $user The user authorizing the application. 646 */ 647 do_action( 'wp_authorize_application_password_request_errors', $error, $request, $user ); 648 649 if ( $error->has_errors() ) { 650 return $error; 651 } 652 653 return true; 654 } -
trunk/src/wp-admin/user-edit.php
r47808 r49109 27 27 28 28 wp_enqueue_script( 'user-profile' ); 29 30 if ( wp_is_application_passwords_available_for_user( $user_id ) ) { 31 wp_enqueue_script( 'application-passwords' ); 32 } 29 33 30 34 if ( IS_PROFILE_PAGE ) { … … 703 707 </table> 704 708 709 710 <?php if ( wp_is_application_passwords_available_for_user( $user_id ) ) : ?> 711 <div class="application-passwords hide-if-no-js" id="application-passwords-section"> 712 <h2><?php _e( 'Application Passwords' ); ?></h2> 713 <p><?php _e( 'Application passwords allow authentication via non-interactive systems, such as XML-RPC or the REST API, without providing your actual password. Application passwords can be easily revoked. They cannot be used for traditional logins to your website.' ); ?></p> 714 <div class="create-application-password"> 715 <label for="new_application_password_name" class="screen-reader-text"><?php _e( 'New Application Password Name' ); ?></label> 716 <input type="text" size="30" id="new_application_password_name" name="new_application_password_name" placeholder="<?php esc_attr_e( 'New Application Password Name' ); ?>" class="input" /> 717 718 <?php 719 /** 720 * Fires in the create Application Passwords form. 721 * 722 * @since 5.6.0 723 * 724 * @param WP_User $profileuser The current WP_User object. 725 */ 726 do_action( 'wp_create_application_password_form', $profileuser ); 727 ?> 728 729 <?php submit_button( __( 'Add New' ), 'secondary', 'do_new_application_password', false ); ?> 730 </div> 731 732 <div class="application-passwords-list-table-wrapper"> 733 <?php 734 $application_passwords_list_table = _get_list_table( 'WP_Application_Passwords_List_Table', array( 'screen' => 'application-passwords-user' ) ); 735 $application_passwords_list_table->prepare_items(); 736 $application_passwords_list_table->display(); 737 ?> 738 </div> 739 </div> 740 <?php endif; ?> 741 705 742 <?php 706 743 if ( IS_PROFILE_PAGE ) { … … 788 825 } 789 826 </script> 827 828 <?php if ( isset( $application_passwords_list_table ) ) : ?> 829 <script type="text/html" id="tmpl-new-application-password"> 830 <div class="notice notice-success is-dismissible new-application-password-notice" role="alert" tabindex="0"> 831 <p> 832 <?php 833 printf( 834 /* translators: 1: Application name, 2: Generated password. */ 835 esc_html__( 'Your new password for %1$s is: %2$s' ), 836 '<strong>{{ data.name }}</strong>', 837 '<kbd>{{ data.password }}</kbd>' 838 ); 839 ?> 840 </p> 841 <p><?php esc_attr_e( 'Be sure to save this in a safe location. You will not be able to retrieve it.' ); ?></p> 842 <button type="button" class="notice-dismiss"> 843 <span class="screen-reader-text"><?php __( 'Dismiss this notice.' ); ?></span> 844 </button> 845 </div> 846 </script> 847 848 <script type="text/html" id="tmpl-application-password-row"> 849 <?php $application_passwords_list_table->print_js_template_row(); ?> 850 </script> 851 <?php endif; ?> 790 852 <?php 791 853 require_once ABSPATH . 'wp-admin/admin-footer.php'; -
trunk/src/wp-includes/class-wp-rewrite.php
r48585 r49109 1510 1510 $rules = "<IfModule mod_rewrite.c>\n"; 1511 1511 $rules .= "RewriteEngine On\n"; 1512 $rules .= 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; 1512 1513 $rules .= "RewriteBase $home_root\n"; 1513 1514 -
trunk/src/wp-includes/default-filters.php
r48966 r49109 277 277 add_action( 'auth_cookie_bad_hash', 'rest_cookie_collect_status' ); 278 278 add_action( 'auth_cookie_valid', 'rest_cookie_collect_status' ); 279 add_action( 'application_password_failed_authentication', 'rest_application_password_collect_status' ); 280 add_action( 'application_password_did_authenticate', 'rest_application_password_collect_status' ); 281 add_filter( 'rest_authentication_errors', 'rest_application_password_check_errors', 90 ); 279 282 add_filter( 'rest_authentication_errors', 'rest_cookie_check_errors', 100 ); 280 283 … … 428 431 add_filter( 'authenticate', 'wp_authenticate_username_password', 20, 3 ); 429 432 add_filter( 'authenticate', 'wp_authenticate_email_password', 20, 3 ); 433 add_filter( 'authenticate', 'wp_authenticate_application_password', 20, 3 ); 430 434 add_filter( 'authenticate', 'wp_authenticate_spam_check', 99 ); 431 435 add_filter( 'determine_current_user', 'wp_validate_auth_cookie' ); 432 436 add_filter( 'determine_current_user', 'wp_validate_logged_in_cookie', 20 ); 437 add_filter( 'determine_current_user', 'wp_validate_application_password', 20 ); 433 438 434 439 // Split term updates. -
trunk/src/wp-includes/load.php
r49023 r49109 87 87 $PHP_SELF = $_SERVER['PHP_SELF']; 88 88 } 89 90 wp_populate_basic_auth_from_authorization_header(); 91 } 92 93 /** 94 * Populates the Basic Auth server details from the Authorization header. 95 * 96 * Some servers running in CGI or FastCGI mode don't pass the Authorization 97 * header on to WordPress. If it's been rewritten to the `HTTP_AUTHORIZATION` header, 98 * fill in the proper $_SERVER variables instead. 99 * 100 * @since 5.6.0 101 */ 102 function wp_populate_basic_auth_from_authorization_header() { 103 // If we don't have anything to pull from, return early. 104 if ( ! isset( $_SERVER['HTTP_AUTHORIZATION'] ) && ! isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ) { 105 return; 106 } 107 108 // If either PHP_AUTH key is already set, do nothing. 109 if ( isset( $_SERVER['PHP_AUTH_USER'] ) || isset( $_SERVER['PHP_AUTH_PW'] ) ) { 110 return; 111 } 112 113 // From our prior conditional, one of these must be set. 114 $header = isset( $_SERVER['HTTP_AUTHORIZATION'] ) ? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; 115 116 // Test to make sure the pattern matches expected. 117 if ( ! preg_match( '%^Basic [a-z\d/+]*={0,2}$%i', $header ) ) { 118 return; 119 } 120 121 // Removing `Basic ` the token would start six characters in. 122 $token = substr( $header, 6 ); 123 $userpass = base64_decode( $token ); 124 125 list( $user, $pass ) = explode( ':', $userpass ); 126 127 // Now shove them in the proper keys where we're expecting later on. 128 $_SERVER['PHP_AUTH_USER'] = $user; 129 $_SERVER['PHP_AUTH_PW'] = $pass; 89 130 } 90 131 -
trunk/src/wp-includes/rest-api.php
r49108 r49109 210 210 211 211 add_filter( 'rest_pre_dispatch', 'rest_handle_options_request', 10, 3 ); 212 add_filter( 'rest_index', 'rest_add_application_passwords_to_index' ); 212 213 } 213 214 … … 263 264 // Users. 264 265 $controller = new WP_REST_Users_Controller; 266 $controller->register_routes(); 267 268 // Application Passwords 269 $controller = new WP_REST_Application_Passwords_Controller(); 265 270 $controller->register_routes(); 266 271 … … 311 316 $controller = new WP_REST_Block_Directory_Controller(); 312 317 $controller->register_routes(); 313 314 318 } 315 319 … … 1033 1037 1034 1038 $wp_rest_auth_cookie = true; 1039 } 1040 1041 /** 1042 * Collects the status of authenticating with an application password. 1043 * 1044 * @since 5.6.0 1045 * 1046 * @global WP_User|WP_Error|null $wp_rest_application_password_status 1047 * 1048 * @param WP_Error $user_or_error The authenticated user or error instance. 1049 */ 1050 function rest_application_password_collect_status( $user_or_error ) { 1051 global $wp_rest_application_password_status; 1052 1053 $wp_rest_application_password_status = $user_or_error; 1054 } 1055 1056 /** 1057 * Checks for errors when using application password-based authentication. 1058 * 1059 * @since 5.6.0 1060 * 1061 * @global WP_User|WP_Error|null $wp_rest_application_password_status 1062 * 1063 * @param WP_Error|null|true $result Error from another authentication handler, 1064 * null if we should handle it, or another value if not. 1065 * @return WP_Error|null|true WP_Error if the application password is invalid, the $result, otherwise true. 1066 */ 1067 function rest_application_password_check_errors( $result ) { 1068 global $wp_rest_application_password_status; 1069 1070 if ( ! empty( $result ) ) { 1071 return $result; 1072 } 1073 1074 if ( is_wp_error( $wp_rest_application_password_status ) ) { 1075 $data = $wp_rest_application_password_status->get_error_data(); 1076 1077 if ( ! isset( $data['status'] ) ) { 1078 $data['status'] = 401; 1079 } 1080 1081 $wp_rest_application_password_status->add_data( $data ); 1082 1083 return $wp_rest_application_password_status; 1084 } 1085 1086 if ( $wp_rest_application_password_status instanceof WP_User ) { 1087 return true; 1088 } 1089 1090 return $result; 1091 } 1092 1093 /** 1094 * Adds Application Passwords info to the REST API index. 1095 * 1096 * @since 5.6.0 1097 * 1098 * @param WP_REST_Response $response The index response object. 1099 * @return WP_REST_Response 1100 */ 1101 function rest_add_application_passwords_to_index( $response ) { 1102 if ( ! wp_is_application_passwords_available() ) { 1103 return $response; 1104 } 1105 1106 $response->data['authentication']['application-passwords'] = array( 1107 'endpoints' => array( 1108 'authorization' => admin_url( 'authorize-application.php' ), 1109 ), 1110 ); 1111 1112 return $response; 1035 1113 } 1036 1114 -
trunk/src/wp-includes/rest-api/class-wp-rest-server.php
r49075 r49109 224 224 * @see WP_REST_Server::dispatch() 225 225 * 226 * @global WP_User $current_user The currently authenticated user. 227 * 226 228 * @param string $path Optional. The request route. If not set, `$_SERVER['PATH_INFO']` will be used. 227 229 * Default null. … … 229 231 */ 230 232 public function serve_request( $path = null ) { 233 /* @var WP_User|null $current_user */ 234 global $current_user; 235 236 if ( $current_user instanceof WP_User && ! $current_user->exists() ) { 237 /* 238 * If there is no current user authenticated via other means, clear 239 * the cached lack of user, so that an authenticate check can set it 240 * properly. 241 * 242 * This is done because for authentications such as Application 243 * Passwords, we don't want it to be accepted unless the current HTTP 244 * request is an API request, which can't always be identified early 245 * enough in evaluation. 246 */ 247 $current_user = null; 248 } 249 231 250 $content_type = isset( $_GET['_jsonp'] ) ? 'application/javascript' : 'application/json'; 232 251 $this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) ); -
trunk/src/wp-includes/script-loader.php
r49104 r49109 1068 1068 $scripts->set_translations( 'password-strength-meter' ); 1069 1069 1070 $scripts->add( 'application-passwords', "/wp-admin/js/application-passwords$suffix.js", array( 'jquery', 'wp-util', 'wp-api-request', 'wp-date', 'wp-i18n', 'wp-hooks', 'wp-a11y' ), false, 1 ); 1071 $scripts->set_translations( 'application-passwords' ); 1072 1073 $scripts->add( 'auth-app', "/wp-admin/js/auth-app$suffix.js", array( 'jquery', 'wp-api-request', 'wp-i18n', 'wp-hooks' ), false, 1 ); 1074 $scripts->set_translations( 'auth-app' ); 1075 1070 1076 $scripts->add( 'user-profile', "/wp-admin/js/user-profile$suffix.js", array( 'jquery', 'password-strength-meter', 'wp-util' ), false, 1 ); 1071 1077 $scripts->set_translations( 'user-profile' ); -
trunk/src/wp-includes/user.php
r49090 r49109 296 296 297 297 return $user; 298 } 299 300 /** 301 * Authenticates the user using an application password. 302 * 303 * @since 5.6.0 304 * 305 * @param WP_User|WP_Error|null $input_user WP_User or WP_Error object if a previous 306 * callback failed authentication. 307 * @param string $username Username for authentication. 308 * @param string $password Password for authentication. 309 * @return WP_User|WP_Error WP_User on success, WP_Error on failure. 310 */ 311 function wp_authenticate_application_password( $input_user, $username, $password ) { 312 if ( $input_user instanceof WP_User ) { 313 return $input_user; 314 } 315 316 $is_api_request = ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ); 317 318 /** 319 * Filters whether this is an API request that Application Passwords can be used on. 320 * 321 * By default, Application Passwords is available for the REST API and XML-RPC. 322 * 323 * @since 5.6.0 324 * 325 * @param bool $is_api_request If this is an acceptable API request. 326 */ 327 $is_api_request = apply_filters( 'application_password_is_api_request', $is_api_request ); 328 329 if ( ! $is_api_request ) { 330 return $input_user; 331 } 332 333 $error = null; 334 $user = get_user_by( 'login', $username ); 335 336 if ( ! $user && is_email( $username ) ) { 337 $user = get_user_by( 'email', $username ); 338 } 339 340 // If the login name is invalid, short circuit. 341 if ( ! $user ) { 342 if ( is_email( $username ) ) { 343 $error = new WP_Error( 344 'invalid_email', 345 __( 'Unknown email address. Check again or try your username.' ) 346 ); 347 } else { 348 $error = new WP_Error( 349 'invalid_username', 350 __( 'Unknown username. Check again or try your email address.' ) 351 ); 352 } 353 } elseif ( ! wp_is_application_passwords_available_for_user( $user ) ) { 354 $error = new WP_Error( 355 'application_passwords_disabled', 356 __( 'Application passwords are disabled for the requested user.' ) 357 ); 358 } 359 360 if ( $error ) { 361 /** 362 * Fires when an application password failed to authenticate the user. 363 * 364 * @since 5.6.0 365 * 366 * @param WP_Error $error The authentication error. 367 */ 368 do_action( 'application_password_failed_authentication', $error ); 369 370 return $error; 371 } 372 373 /* 374 * Strip out anything non-alphanumeric. This is so passwords can be used with 375 * or without spaces to indicate the groupings for readability. 376 * 377 * Generated application passwords are exclusively alphanumeric. 378 */ 379 $password = preg_replace( '/[^a-z\d]/i', '', $password ); 380 381 $hashed_passwords = WP_Application_Passwords::get_user_application_passwords( $user->ID ); 382 383 foreach ( $hashed_passwords as $key => $item ) { 384 if ( ! wp_check_password( $password, $item['password'], $user->ID ) ) { 385 continue; 386 } 387 388 $error = new WP_Error(); 389 390 /** 391 * Fires when an application password has been successfully checked as valid. 392 * 393 * This allows for plugins to add additional constraints to prevent an application password from being used. 394 * 395 * @since 5.6.0 396 * 397 * @param WP_Error $error The error object. 398 * @param WP_User $user The user authenticating. 399 * @param array $item The details about the application password. 400 * @param string $password The raw supplied password. 401 */ 402 do_action( 'wp_authenticate_application_password_errors', $error, $user, $item, $password ); 403 404 if ( is_wp_error( $error ) && $error->has_errors() ) { 405 /** This action is documented in wp-includes/user.php */ 406 do_action( 'application_password_failed_authentication', $error ); 407 408 return $error; 409 } 410 411 WP_Application_Passwords::record_application_password_usage( $user->ID, $item['uuid'] ); 412 413 /** 414 * Fires after an application password was used for authentication. 415 * 416 * @since 5.6.0 417 * 418 * @param WP_User $user The user who was authenticated. 419 * @param array $item The application password used. 420 */ 421 do_action( 'application_password_did_authenticate', $user, $item ); 422 423 return $user; 424 } 425 426 $error = new WP_Error( 427 'incorrect_password', 428 __( 'The provided password is an invalid application password.' ) 429 ); 430 431 /** This action is documented in wp-includes/user.php */ 432 do_action( 'application_password_failed_authentication', $error ); 433 434 return $error; 435 } 436 437 /** 438 * Validates the application password credentials passed via Basic Authentication. 439 * 440 * @since 5.6.0 441 * 442 * @param int|bool $input_user User ID if one has been determined, false otherwise. 443 * @return int|bool The authenticated user ID if successful, false otherwise. 444 */ 445 function wp_validate_application_password( $input_user ) { 446 // Don't authenticate twice. 447 if ( ! empty( $input_user ) ) { 448 return $input_user; 449 } 450 451 if ( ! wp_is_application_passwords_available() ) { 452 return $input_user; 453 } 454 455 // Check that we're trying to authenticate 456 if ( ! isset( $_SERVER['PHP_AUTH_USER'] ) ) { 457 return $input_user; 458 } 459 460 $authenticated = wp_authenticate_application_password( null, $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ); 461 462 if ( $authenticated instanceof WP_User ) { 463 return $authenticated->ID; 464 } 465 466 // If it wasn't a user what got returned, just pass on what we had received originally. 467 return $input_user; 298 468 } 299 469 … … 3924 4094 return new WP_User_Request( $post ); 3925 4095 } 4096 4097 /** 4098 * Checks if Application Passwords is globally available. 4099 * 4100 * By default, Application Passwords is available to all sites using SSL, but this function is 4101 * filterable to adjust its availability. 4102 * 4103 * @since 5.6.0 4104 * 4105 * @return bool 4106 */ 4107 function wp_is_application_passwords_available() { 4108 /** 4109 * Filters whether Application Passwords is available. 4110 * 4111 * @since 5.6.0 4112 * 4113 * @param bool $available True if available, false otherwise. 4114 */ 4115 return apply_filters( 'wp_is_application_passwords_available', is_ssl() ); 4116 } 4117 4118 /** 4119 * Checks if Application Passwords is enabled for a specific user. 4120 * 4121 * By default all users can use Application Passwords, but this function is filterable to restrict 4122 * availability to certain users. 4123 * 4124 * @since 5.6.0 4125 * 4126 * @param int|WP_User $user The user to check. 4127 * @return bool 4128 */ 4129 function wp_is_application_passwords_available_for_user( $user ) { 4130 if ( ! wp_is_application_passwords_available() ) { 4131 return false; 4132 } 4133 4134 if ( ! is_object( $user ) ) { 4135 $user = get_userdata( $user ); 4136 } 4137 4138 if ( ! $user || ! $user->exists() ) { 4139 return false; 4140 } 4141 4142 /** 4143 * Filters whether Application Passwords is available for a specific user. 4144 * 4145 * @since 5.6.0 4146 * 4147 * @param bool $available True if available, false otherwise. 4148 * @param WP_User $user The user to check. 4149 */ 4150 return apply_filters( 'wp_is_application_passwords_available_for_user', true, $user ); 4151 } -
trunk/src/wp-login.php
r49078 r49109 1372 1372 } elseif ( WP_Recovery_Mode_Link_Service::LOGIN_ACTION_ENTERED === $action ) { 1373 1373 $errors->add( 'enter_recovery_mode', __( 'Recovery Mode Initialized. Please log in to continue.' ), 'message' ); 1374 } elseif ( isset( $_GET['redirect_to'] ) && false !== strpos( $_GET['redirect_to'], 'wp-admin/authorize-application.php' ) ) { 1375 $query_component = wp_parse_url( $_GET['redirect_to'], PHP_URL_QUERY ); 1376 parse_str( $query_component, $query ); 1377 1378 if ( ! empty( $query['app_name'] ) ) { 1379 /* translators: 1: Website name, 2: Application name. */ 1380 $message = sprintf( 'Please log in to %1$s to authorize %2$s to connect to your account.', get_bloginfo( 'name', 'display' ), '<strong>' . esc_html( $query['app_name'] ) . '</strong>' ); 1381 } else { 1382 /* translators: Website name. */ 1383 $message = sprintf( 'Please log in to %s to proceed with authorization.', get_bloginfo( 'name', 'display' ) ); 1384 } 1385 1386 $errors->add( 'authorize_application', $message, 'message' ); 1374 1387 } 1375 1388 } -
trunk/src/wp-settings.php
r49103 r49109 237 237 require ABSPATH . WPINC . '/nav-menu-template.php'; 238 238 require ABSPATH . WPINC . '/admin-bar.php'; 239 require ABSPATH . WPINC . '/class-wp-application-passwords.php'; 239 240 require ABSPATH . WPINC . '/rest-api.php'; 240 241 require ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php'; … … 260 261 require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-plugins-controller.php'; 261 262 require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-block-directory-controller.php'; 263 require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-application-passwords-controller.php'; 262 264 require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php'; 263 265 require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.php'; -
trunk/tests/phpunit/tests/auth.php
r48937 r49109 7 7 class Tests_Auth extends WP_UnitTestCase { 8 8 protected $user; 9 10 /** 11 * @var WP_User 12 */ 9 13 protected static $_user; 10 14 protected static $user_id; … … 34 38 $this->user = clone self::$_user; 35 39 wp_set_current_user( self::$user_id ); 40 } 41 42 public function tearDown() { 43 parent::tearDown(); 44 45 // Cleanup all the global state. 46 unset( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'], $GLOBALS['wp_rest_application_password_status'] ); 36 47 } 37 48 … … 415 426 } 416 427 428 /** 429 * HTTP Auth headers are used to determine the current user. 430 * 431 * @ticket 42790 432 * 433 * @covers ::wp_validate_application_password 434 */ 435 public function test_application_password_authentication() { 436 $user_id = $this->factory()->user->create( 437 array( 438 'user_login' => 'http_auth_login', 439 'user_pass' => 'http_auth_pass', // Shouldn't be allowed for API login. 440 ) 441 ); 442 443 // Create a new app-only password. 444 list( $user_app_password ) = WP_Application_Passwords::create_new_application_password( $user_id, array( 'name' => 'phpunit' ) ); 445 446 // Fake a REST API request. 447 add_filter( 'application_password_is_api_request', '__return_true' ); 448 add_filter( 'wp_is_application_passwords_available', '__return_true' ); 449 450 // Fake an HTTP Auth request with the regular account password first. 451 $_SERVER['PHP_AUTH_USER'] = 'http_auth_login'; 452 $_SERVER['PHP_AUTH_PW'] = 'http_auth_pass'; 453 454 $this->assertEquals( 455 0, 456 wp_validate_application_password( null ), 457 'Regular user account password should not be allowed for API authentication' 458 ); 459 460 // Not try with an App password instead. 461 $_SERVER['PHP_AUTH_PW'] = $user_app_password; 462 463 $this->assertEquals( 464 $user_id, 465 wp_validate_application_password( null ), 466 'Application passwords should be allowed for API authentication' 467 ); 468 } 469 470 /** 471 * @ticket 42790 472 */ 473 public function test_authenticate_application_password_respects_existing_user() { 474 $this->assertSame( self::$_user, wp_authenticate_application_password( self::$_user, self::$_user->user_login, 'password' ) ); 475 } 476 477 /** 478 * @ticket 42790 479 */ 480 public function test_authenticate_application_password_is_rejected_if_not_api_request() { 481 add_filter( 'application_password_is_api_request', '__return_false' ); 482 483 $this->assertNull( wp_authenticate_application_password( null, self::$_user->user_login, 'password' ) ); 484 } 485 486 /** 487 * @ticket 42790 488 */ 489 public function test_authenticate_application_password_invalid_username() { 490 add_filter( 'application_password_is_api_request', '__return_true' ); 491 492 $error = wp_authenticate_application_password( null, 'idonotexist', 'password' ); 493 $this->assertWPError( $error ); 494 $this->assertEquals( 'invalid_username', $error->get_error_code() ); 495 } 496 497 /** 498 * @ticket 42790 499 */ 500 public function test_authenticate_application_password_invalid_email() { 501 add_filter( 'application_password_is_api_request', '__return_true' ); 502 503 $error = wp_authenticate_application_password( null, 'idonotexist@example.org', 'password' ); 504 $this->assertWPError( $error ); 505 $this->assertEquals( 'invalid_email', $error->get_error_code() ); 506 } 507 508 /** 509 * @ticket 42790 510 */ 511 public function test_authenticate_application_password_not_allowed() { 512 add_filter( 'application_password_is_api_request', '__return_true' ); 513 add_filter( 'wp_is_application_passwords_available', '__return_false' ); 514 515 $error = wp_authenticate_application_password( null, self::$_user->user_login, 'password' ); 516 $this->assertWPError( $error ); 517 $this->assertEquals( 'application_passwords_disabled', $error->get_error_code() ); 518 } 519 520 /** 521 * @ticket 42790 522 */ 523 public function test_authenticate_application_password_not_allowed_for_user() { 524 add_filter( 'application_password_is_api_request', '__return_true' ); 525 add_filter( 'wp_is_application_passwords_available', '__return_true' ); 526 add_filter( 'wp_is_application_passwords_available_for_user', '__return_false' ); 527 528 $error = wp_authenticate_application_password( null, self::$_user->user_login, 'password' ); 529 $this->assertWPError( $error ); 530 $this->assertEquals( 'application_passwords_disabled', $error->get_error_code() ); 531 } 532 533 /** 534 * @ticket 42790 535 */ 536 public function test_authenticate_application_password_incorrect_password() { 537 add_filter( 'application_password_is_api_request', '__return_true' ); 538 add_filter( 'wp_is_application_passwords_available', '__return_true' ); 539 540 $error = wp_authenticate_application_password( null, self::$_user->user_login, 'password' ); 541 $this->assertWPError( $error ); 542 $this->assertEquals( 'incorrect_password', $error->get_error_code() ); 543 } 544 545 /** 546 * @ticket 42790 547 */ 548 public function test_authenticate_application_password_custom_errors() { 549 add_filter( 'application_password_is_api_request', '__return_true' ); 550 add_filter( 'wp_is_application_passwords_available', '__return_true' ); 551 552 add_action( 553 'wp_authenticate_application_password_errors', 554 static function ( WP_Error $error ) { 555 $error->add( 'my_code', 'My Error' ); 556 } 557 ); 558 559 list( $password ) = WP_Application_Passwords::create_new_application_password( self::$user_id, array( 'name' => 'phpunit' ) ); 560 561 $error = wp_authenticate_application_password( null, self::$_user->user_login, $password ); 562 $this->assertWPError( $error ); 563 $this->assertEquals( 'my_code', $error->get_error_code() ); 564 } 565 566 /** 567 * @ticket 42790 568 */ 569 public function test_authenticate_application_password_by_username() { 570 add_filter( 'application_password_is_api_request', '__return_true' ); 571 add_filter( 'wp_is_application_passwords_available', '__return_true' ); 572 573 list( $password ) = WP_Application_Passwords::create_new_application_password( self::$user_id, array( 'name' => 'phpunit' ) ); 574 575 $user = wp_authenticate_application_password( null, self::$_user->user_login, $password ); 576 $this->assertInstanceOf( WP_User::class, $user ); 577 $this->assertEquals( self::$user_id, $user->ID ); 578 } 579 580 /** 581 * @ticket 42790 582 */ 583 public function test_authenticate_application_password_by_email() { 584 add_filter( 'application_password_is_api_request', '__return_true' ); 585 add_filter( 'wp_is_application_passwords_available', '__return_true' ); 586 587 list( $password ) = WP_Application_Passwords::create_new_application_password( self::$user_id, array( 'name' => 'phpunit' ) ); 588 589 $user = wp_authenticate_application_password( null, self::$_user->user_email, $password ); 590 $this->assertInstanceOf( WP_User::class, $user ); 591 $this->assertEquals( self::$user_id, $user->ID ); 592 } 593 594 /** 595 * @ticket 42790 596 */ 597 public function test_authenticate_application_password_chunked() { 598 add_filter( 'application_password_is_api_request', '__return_true' ); 599 add_filter( 'wp_is_application_passwords_available', '__return_true' ); 600 601 list( $password ) = WP_Application_Passwords::create_new_application_password( self::$user_id, array( 'name' => 'phpunit' ) ); 602 603 $user = wp_authenticate_application_password( null, self::$_user->user_email, WP_Application_Passwords::chunk_password( $password ) ); 604 $this->assertInstanceOf( WP_User::class, $user ); 605 $this->assertEquals( self::$user_id, $user->ID ); 606 } 417 607 } -
trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php
r48937 r49109 119 119 '/wp/v2/users/(?P<id>[\\d]+)', 120 120 '/wp/v2/users/me', 121 '/wp/v2/users/(?P<user_id>(?:[\\d]+|me))/application-passwords', 122 '/wp/v2/users/(?P<user_id>(?:[\\d]+|me))/application-passwords/(?P<uuid>[\\w\\-]+)', 121 123 '/wp/v2/comments', 122 124 '/wp/v2/comments/(?P<id>[\\d]+)', … … 133 135 ); 134 136 135 $this->assertSame ( $expected_routes, $routes );137 $this->assertSameSets( $expected_routes, $routes ); 136 138 } 137 139 -
trunk/tests/qunit/fixtures/wp-api-generated.js
r49103 r49109 3985 3985 "self": "http://example.org/index.php?rest_route=/wp/v2/users/me" 3986 3986 } 3987 }, 3988 "/wp/v2/users/(?P<user_id>(?:[\\d]+|me))/application-passwords": { 3989 "namespace": "wp/v2", 3990 "methods": [ 3991 "GET", 3992 "POST", 3993 "DELETE" 3994 ], 3995 "endpoints": [ 3996 { 3997 "methods": [ 3998 "GET" 3999 ], 4000 "args": { 4001 "context": { 4002 "required": false, 4003 "default": "view", 4004 "enum": [ 4005 "view", 4006 "embed", 4007 "edit" 4008 ], 4009 "description": "Scope under which the request is made; determines fields present in response.", 4010 "type": "string" 4011 } 4012 } 4013 }, 4014 { 4015 "methods": [ 4016 "POST" 4017 ], 4018 "args": { 4019 "name": { 4020 "required": true, 4021 "description": "The name of the application password.", 4022 "type": "string" 4023 } 4024 } 4025 }, 4026 { 4027 "methods": [ 4028 "DELETE" 4029 ], 4030 "args": [] 4031 } 4032 ] 4033 }, 4034 "/wp/v2/users/(?P<user_id>(?:[\\d]+|me))/application-passwords/(?P<uuid>[\\w\\-]+)": { 4035 "namespace": "wp/v2", 4036 "methods": [ 4037 "GET", 4038 "POST", 4039 "PUT", 4040 "PATCH", 4041 "DELETE" 4042 ], 4043 "endpoints": [ 4044 { 4045 "methods": [ 4046 "GET" 4047 ], 4048 "args": { 4049 "context": { 4050 "required": false, 4051 "default": "view", 4052 "enum": [ 4053 "view", 4054 "embed", 4055 "edit" 4056 ], 4057 "description": "Scope under which the request is made; determines fields present in response.", 4058 "type": "string" 4059 } 4060 } 4061 }, 4062 { 4063 "methods": [ 4064 "POST", 4065 "PUT", 4066 "PATCH" 4067 ], 4068 "args": { 4069 "name": { 4070 "required": false, 4071 "description": "The name of the application password.", 4072 "type": "string" 4073 } 4074 } 4075 }, 4076 { 4077 "methods": [ 4078 "DELETE" 4079 ], 4080 "args": [] 4081 } 4082 ] 3987 4083 }, 3988 4084 "/wp/v2/comments": {
Note: See TracChangeset
for help on using the changeset viewer.