Ticket #46595: 46595.6.patch
File 46595.6.patch, 9.2 KB (added by , 6 years ago) |
---|
-
src/wp-includes/class-wp-recovery-mode-key-service.php
19 19 * @since 5.2.0 20 20 * 21 21 * @global PasswordHash $wp_hasher 22 * 23 * @return string Recovery mode key. 22 * @return array Recovery mode key and Token. 24 23 */ 25 24 public function generate_and_store_recovery_mode_key() { 26 25 … … 44 43 45 44 $hashed = $wp_hasher->HashPassword( $key ); 46 45 47 update_option( 48 'recovery_key', 49 array( 50 'hashed_key' => $hashed, 51 'created_at' => time(), 52 ) 46 $records = get_option( 'recovery_key', array() ); 47 48 $token = wp_generate_password( 22, false ); 49 50 $records[ $token ] = array( 51 'hashed_key' => $hashed, 52 'created_at' => time(), 53 53 ); 54 54 55 return $key; 55 update_option( 'recovery_key', $records ); 56 57 return array( $key, $token ); 56 58 } 57 59 58 60 /** 59 61 * Verifies if the recovery mode key is correct. 62 * Removes any old keys and the key passed to it 60 63 * 61 64 * @since 5.2.0 62 65 * 63 66 * @param string $key The unhashed key. 67 * @param string $token The data index 64 68 * @param int $ttl Time in seconds for the key to be valid for. 65 69 * @return true|WP_Error True on success, error object on failure. 66 70 */ 67 public function validate_ recovery_mode_key( $key, $ttl ) {71 public function validate_and_consume_recovery_mode_key( $key, $token, $ttl ) { 68 72 69 $record = get_option( 'recovery_key' );73 $records = get_option( 'recovery_key' ); 70 74 71 if ( ! $record ) {75 if ( ! $records ) { 72 76 return new WP_Error( 'no_recovery_key_set', __( 'Recovery Mode not initialized.' ) ); 73 77 } 74 78 79 if ( ! isset( $records[ $token ] ) ) { 80 return new WP_Error( 'recovery_key_data_missing', __( 'Recovery Mode not initialized.' ) ); 81 } 82 83 $record = $records[ $token ]; 84 85 // remove so it a onetime use 86 $this->clean_recovery_key_options( $ttl, $token ); 87 75 88 if ( ! is_array( $record ) || ! isset( $record['hashed_key'], $record['created_at'] ) ) { 76 89 return new WP_Error( 'invalid_recovery_key_format', __( 'Invalid recovery key format.' ) ); 77 90 } … … 86 99 87 100 return true; 88 101 } 102 103 /** 104 * Removes old and used recovery keys. 105 * 106 * @since 5.2.0 107 * 108 * @param int $ttl Unix timestamp 109 * @param string $token The data index 110 */ 111 public function clean_recovery_key_options( $ttl, $token = '' ) { 112 113 $records = get_option( 'recovery_key' ); 114 115 if ( '' !== $token ) { 116 unset( $records[ $token ] ); 117 } 118 119 foreach ( $records as $key => $record ) { 120 if ( ! isset( $record['created_at'] ) || time() > $record['created_at'] + $ttl ) { 121 unset( $records[ $key ] ); 122 } 123 } 124 125 update_option( 'recovery_key', $records ); 126 } 89 127 } -
src/wp-includes/functions.php
797 797 */ 798 798 function is_new_day() { 799 799 global $currentday, $previousday; 800 if ( $currentday != $previousday ) {800 if ( $currentday !== $previousday ) { 801 801 return 1; 802 802 } else { 803 803 return 0; -
tests/phpunit/tests/error-protection/recovery-mode-key-service.php
20 20 */ 21 21 public function test_validate_recovery_mode_key_returns_wp_error_if_no_key_set() { 22 22 $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 ); 24 24 25 25 $this->assertWPError( $error ); 26 26 $this->assertEquals( 'no_recovery_key_set', $error->get_error_code() ); 27 27 } 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' ); 28 33 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 29 41 /** 30 42 * @ticket 46130 31 43 */ 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 */ 32 58 public function test_validate_recovery_mode_key_returns_wp_error_if_stored_format_is_invalid() { 33 update_option( 'recovery_key', 'gibberish' );34 59 60 $token = wp_generate_password( 22, false ); 61 update_option( 'recovery_key', array( $token => 'gibberish' ) ); 62 35 63 $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 ); 37 65 38 66 $this->assertWPError( $error ); 39 67 $this->assertEquals( 'invalid_recovery_key_format', $error->get_error_code() ); … … 44 72 */ 45 73 public function test_validate_recovery_mode_key_returns_wp_error_if_empty_key() { 46 74 $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 ); 49 77 50 78 $this->assertWPError( $error ); 51 79 $this->assertEquals( 'hash_mismatch', $error->get_error_code() ); … … 56 84 */ 57 85 public function test_validate_recovery_mode_key_returns_wp_error_if_hash_mismatch() { 58 86 $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 ); 61 89 62 90 $this->assertWPError( $error ); 63 91 $this->assertEquals( 'hash_mismatch', $error->get_error_code() ); … … 68 96 */ 69 97 public function test_validate_recovery_mode_key_returns_wp_error_if_expired() { 70 98 $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(); 72 100 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 ); 76 104 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 ); 78 106 79 107 $this->assertWPError( $error ); 80 108 $this->assertEquals( 'key_expired', $error->get_error_code() ); … … 85 113 */ 86 114 public function test_validate_recovery_mode_key_returns_true_for_valid_key() { 87 115 $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 ) ); 90 118 } 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 } 91 157 }