Make WordPress Core

Ticket #46595: 46595.5.patch

File 46595.5.patch, 11.1 KB (added by pbearne, 6 years ago)

Code style fixes

  • src/wp-includes/class-wp-recovery-mode-key-service.php

     
    2020         *
    2121         * @global PasswordHash $wp_hasher
    2222         *
    23          * @return string Recovery mode key.
     23         * @return array Recovery mode key and Token.
    2424         */
    2525        public function generate_and_store_recovery_mode_key() {
    2626
     
    4444
    4545                $hashed = $wp_hasher->HashPassword( $key );
    4646
    47                 update_option(
    48                         'recovery_key',
    49                         array(
    50                                 'hashed_key' => $hashed,
    51                                 'created_at' => time(),
    52                         )
     47                $records = get_option( 'recovery_key', false );
     48
     49                if ( false === $records ) {
     50
     51                        $records = array();
     52                }
     53
     54                $token = wp_generate_password( 22, false );
     55
     56                $records[ $token ] = array(
     57                        'hashed_key' => $hashed,
     58                        'created_at' => time(),
    5359                );
    5460
    55                 return $key;
     61                update_option( 'recovery_key', $records );
     62
     63                return array( $key, $token );
    5664        }
    5765
    5866        /**
    5967         * Verifies if the recovery mode key is correct.
     68         * Removes any old keys and the key passed to it
    6069         *
    6170         * @since 5.2.0
    6271         *
    6372         * @param string $key The unhashed key.
     73         * @param string $token The data index
    6474         * @param int    $ttl Time in seconds for the key to be valid for.
     75         *
    6576         * @return true|WP_Error True on success, error object on failure.
    6677         */
    67         public function validate_recovery_mode_key( $key, $ttl ) {
     78        public function validate_and_consume_recovery_mode_key( $key, $token, $ttl ) {
    6879
    69                 $record = get_option( 'recovery_key' );
     80                $records = get_option( 'recovery_key' );
    7081
    71                 if ( ! $record ) {
     82                if ( ! $records ) {
    7283                        return new WP_Error( 'no_recovery_key_set', __( 'Recovery Mode not initialized.' ) );
    7384                }
    7485
     86                if ( ! isset( $records[ $token ] ) ) {
     87                        return new WP_Error( 'recovery_key_data_missing', __( 'Recovery Mode not initialized.' ) );
     88                }
     89
     90                $record = $records[ $token ];
     91
     92                // remove so it a onetime use
     93                $this->clean_recovery_key_options( $ttl, $token );
     94
    7595                if ( ! is_array( $record ) || ! isset( $record['hashed_key'], $record['created_at'] ) ) {
    7696                        return new WP_Error( 'invalid_recovery_key_format', __( 'Invalid recovery key format.' ) );
    7797                }
     
    86106
    87107                return true;
    88108        }
     109
     110        /**
     111         * Removes old and used recovery keys.
     112         *
     113         * @since 5.2.0
     114         *
     115         * @param int         $ttl Unix timestamp
     116         * @param null/string $token The data index
     117         *
     118         */
     119        public function clean_recovery_key_options(  $ttl, $token = null  ){
     120
     121                $records = get_option( 'recovery_key' );
     122
     123                if ( null !== $token ) {
     124                        unset( $records[ $token ] );
     125                }
     126
     127                foreach ( $records as $key => $record ) {
     128                        if ( ! isset( $record['created_at']) || time() > $record['created_at'] + $ttl ) {
     129                                unset( $records[ $key ] );
     130                        }
     131                }
     132
     133                update_option( 'recovery_key', $records );
     134        }
    89135}
  • src/wp-includes/class-wp-recovery-mode-link-service.php

     
    5353         * @return string Generated URL.
    5454         */
    5555        public function generate_url() {
    56                 $key = $this->key_service->generate_and_store_recovery_mode_key();
     56                list( $key, $token ) = $this->key_service->generate_and_store_recovery_mode_key();
    5757
    58                 return $this->get_recovery_mode_begin_url( $key );
     58                return $this->get_recovery_mode_begin_url( $key, $token );
    5959        }
    6060
    6161        /**
     
    7070                        return;
    7171                }
    7272
    73                 if ( ! isset( $_GET['action'], $_GET['rm_key'] ) || self::LOGIN_ACTION_ENTER !== $_GET['action'] ) {
     73                if ( ! isset( $_GET['action'], $_GET['rm_key'], $_GET['rm_token'] ) || self::LOGIN_ACTION_ENTER !== $_GET['action'] ) {
    7474                        return;
    7575                }
    7676
     
    7878                        require_once ABSPATH . WPINC . '/pluggable.php';
    7979                }
    8080
    81                 $validated = $this->key_service->validate_recovery_mode_key( $_GET['rm_key'], $ttl );
     81                $validated = $this->key_service->validate_and_consume_recovery_mode_key( $_GET['rm_key'], $_GET['rm_token'], $ttl );
    8282
    8383                if ( is_wp_error( $validated ) ) {
    8484                        wp_die( $validated, '' );
     
    9797         * @since 5.2.0
    9898         *
    9999         * @param string $key Recovery Mode key created by {@see generate_and_store_recovery_mode_key()}
     100         *
     101         * @param string $token Recovery Mode data index created by {@see generate_and_store_recovery_mode_key()}
     102         *
    100103         * @return string Recovery mode begin URL.
    101104         */
    102         private function get_recovery_mode_begin_url( $key ) {
     105        private function get_recovery_mode_begin_url( $key, $token ) {
    103106
    104107                $url = add_query_arg(
    105108                        array(
    106                                 'action' => self::LOGIN_ACTION_ENTER,
    107                                 'rm_key' => $key,
     109                                'action'   => self::LOGIN_ACTION_ENTER,
     110                                'rm_key'   => $key,
     111                                'rm_token' => $token,
    108112                        ),
    109113                        wp_login_url()
    110114                );
     
    116120                 *
    117121                 * @param string $url
    118122                 * @param string $key
     123                 * @param string $token
    119124                 */
    120                 return apply_filters( 'recovery_mode_begin_url', $url, $key );
     125                return apply_filters( 'recovery_mode_begin_url', $url, $key, $token );
    121126        }
    122127}
  • tests/phpunit/tests/error-protection/recovery-mode-key-service.php

     
    2020         */
    2121        public function test_validate_recovery_mode_key_returns_wp_error_if_no_key_set() {
    2222                $service = new WP_Recovery_Mode_Key_Service();
    23                 $error   = $service->validate_recovery_mode_key( 'abcd', HOUR_IN_SECONDS );
     23                $error   = $service->validate_and_consume_recovery_mode_key( 'abcd', '', HOUR_IN_SECONDS );
    2424
    2525                $this->assertWPError( $error );
    2626                $this->assertEquals( 'no_recovery_key_set', $error->get_error_code() );
    2727        }
     28        /**
     29         * @ticket 46130
     30         */
     31        public function test_validate_recovery_mode_key_returns_wp_error_if_data_missing() {
     32                update_option( 'recovery_key', 'gibberish' );
    2833
     34                $service = new WP_Recovery_Mode_Key_Service();
     35                $error   = $service->validate_and_consume_recovery_mode_key( 'abcd', '', HOUR_IN_SECONDS );
     36
     37                $this->assertWPError( $error );
     38                $this->assertEquals( 'recovery_key_data_missing', $error->get_error_code() );
     39        }
     40
    2941        /**
    3042         * @ticket 46130
    3143         */
     44        public function test_validate_recovery_mode_key_returns_wp_error_if_bad() {
     45                update_option( 'recovery_key', array( 'token' => 'gibberish' ) );
     46
     47                $service = new WP_Recovery_Mode_Key_Service();
     48                $error   = $service->validate_and_consume_recovery_mode_key( 'abcd', 'token', HOUR_IN_SECONDS );
     49
     50                $this->assertWPError( $error );
     51                $this->assertEquals( 'invalid_recovery_key_format', $error->get_error_code() );
     52        }
     53
     54
     55        /**
     56         * @ticket 46130
     57         */
    3258        public function test_validate_recovery_mode_key_returns_wp_error_if_stored_format_is_invalid() {
    33                 update_option( 'recovery_key', 'gibberish' );
    3459
     60                $token =  wp_generate_password( 22, false );
     61                update_option( 'recovery_key', array( $token => 'gibberish' ) );
     62
    3563                $service = new WP_Recovery_Mode_Key_Service();
    36                 $error   = $service->validate_recovery_mode_key( 'abcd', HOUR_IN_SECONDS );
     64                $error   = $service->validate_and_consume_recovery_mode_key( 'abcd', $token, HOUR_IN_SECONDS );
    3765
    3866                $this->assertWPError( $error );
    3967                $this->assertEquals( 'invalid_recovery_key_format', $error->get_error_code() );
     
    4472         */
    4573        public function test_validate_recovery_mode_key_returns_wp_error_if_empty_key() {
    4674                $service = new WP_Recovery_Mode_Key_Service();
    47                 $service->generate_and_store_recovery_mode_key();
    48                 $error = $service->validate_recovery_mode_key( '', HOUR_IN_SECONDS );
     75                list( $key, $token ) = $service->generate_and_store_recovery_mode_key();
     76                $error = $service->validate_and_consume_recovery_mode_key( '', $token, HOUR_IN_SECONDS );
    4977
    5078                $this->assertWPError( $error );
    5179                $this->assertEquals( 'hash_mismatch', $error->get_error_code() );
     
    5684         */
    5785        public function test_validate_recovery_mode_key_returns_wp_error_if_hash_mismatch() {
    5886                $service = new WP_Recovery_Mode_Key_Service();
    59                 $service->generate_and_store_recovery_mode_key();
    60                 $error = $service->validate_recovery_mode_key( 'abcd', HOUR_IN_SECONDS );
     87                list( $key, $token ) = $service->generate_and_store_recovery_mode_key();
     88                $error = $service->validate_and_consume_recovery_mode_key( 'abcd', $token, HOUR_IN_SECONDS );
    6189
    6290                $this->assertWPError( $error );
    6391                $this->assertEquals( 'hash_mismatch', $error->get_error_code() );
     
    6896         */
    6997        public function test_validate_recovery_mode_key_returns_wp_error_if_expired() {
    7098                $service = new WP_Recovery_Mode_Key_Service();
    71                 $key    = $service->generate_and_store_recovery_mode_key();
     99                list( $key, $token ) = $service->generate_and_store_recovery_mode_key();
    72100
    73                 $record               = get_option( 'recovery_key' );
    74                 $record['created_at'] = time() - HOUR_IN_SECONDS - 30;
    75                 update_option( 'recovery_key', $record );
     101                $records                         = get_option( 'recovery_key' );
     102                $records[ $token ]['created_at'] = time() - HOUR_IN_SECONDS - 30;
     103                update_option( 'recovery_key', $records );
    76104
    77                 $error = $service->validate_recovery_mode_key( $key, HOUR_IN_SECONDS );
     105                $error = $service->validate_and_consume_recovery_mode_key( $key, $token, HOUR_IN_SECONDS );
    78106
    79107                $this->assertWPError( $error );
    80108                $this->assertEquals( 'key_expired', $error->get_error_code() );
     
    85113         */
    86114        public function test_validate_recovery_mode_key_returns_true_for_valid_key() {
    87115                $service = new WP_Recovery_Mode_Key_Service();
    88                 $key    = $service->generate_and_store_recovery_mode_key();
    89                 $this->assertTrue( $service->validate_recovery_mode_key( $key, HOUR_IN_SECONDS ) );
     116                list( $key, $token ) = $service->generate_and_store_recovery_mode_key();
     117                $this->assertTrue( $service->validate_and_consume_recovery_mode_key( $key, $token, HOUR_IN_SECONDS ) );
    90118        }
     119
     120
     121        /**
     122         * @ticket 46130
     123         */
     124        public function test_validate_recovery_mode_key_returns_error_if_token_used_more_than_once() {
     125                $service = new WP_Recovery_Mode_Key_Service();
     126                list( $key, $token ) = $service->generate_and_store_recovery_mode_key();
     127
     128                $this->assertTrue( $service->validate_and_consume_recovery_mode_key( $key, $token, HOUR_IN_SECONDS ) );
     129
     130                // data should be remove by first call
     131                $error  = $service->validate_and_consume_recovery_mode_key( $key, $token, HOUR_IN_SECONDS );
     132
     133                $this->assertWPError( $error );
     134                $this->assertEquals( 'no_recovery_key_set', $error->get_error_code() );
     135        }
     136
     137
     138        /**
     139         * @ticket 46130
     140         */
     141        public function test_validate_recovery_mode_key_returns_error_if_token_used_more_than_once_more_than_key_stored() {
     142                $service = new WP_Recovery_Mode_Key_Service();
     143
     144                //              create an extra key
     145                $service->generate_and_store_recovery_mode_key();
     146
     147                list( $key, $token ) = $service->generate_and_store_recovery_mode_key();
     148
     149                $this->assertTrue( $service->validate_and_consume_recovery_mode_key( $key, $token, HOUR_IN_SECONDS ) );
     150
     151                // data should be remove by first call
     152                $error  = $service->validate_and_consume_recovery_mode_key( $key, $token, HOUR_IN_SECONDS );
     153
     154                $this->assertWPError( $error );
     155                $this->assertEquals( 'recovery_key_data_missing', $error->get_error_code() );
     156        }
    91157}