mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2026-08-11 12:20:22 +00:00
Bootstrap/Load: Allow more than one recovery link to be valid at a time.
While currently a recovery link is only made available via the admin email address, this will be expanded in the future. In order to accomplish that, the mechanisms to store and validate recovery keys must support multiple keys to be valid at the same time. This changeset adds that support, adding an additional token parameter which is part of a recovery link in addition to the key. A key itself is always associated with a token, so the two are only valid in combination. These associations are stored in a new `recovery_keys` option, which is regularly cleared in a new Cron hook, to prevent potential cluttering from unused recovery keys. This changeset does not have any user-facing implications otherwise. Props pbearne, timothyblynjacobs. Fixes #46595. See #46130. git-svn-id: https://develop.svn.wordpress.org/trunk@45211 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
@@ -10,7 +10,8 @@ class Tests_Recovery_Mode_Key_Service extends WP_UnitTestCase {
|
||||
*/
|
||||
public function test_generate_and_store_recovery_mode_key_returns_recovery_key() {
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$key = $service->generate_and_store_recovery_mode_key();
|
||||
$token = $service->generate_recovery_mode_token();
|
||||
$key = $service->generate_and_store_recovery_mode_key( $token );
|
||||
|
||||
$this->assertNotWPError( $key );
|
||||
}
|
||||
@@ -20,20 +21,49 @@ class Tests_Recovery_Mode_Key_Service extends WP_UnitTestCase {
|
||||
*/
|
||||
public function test_validate_recovery_mode_key_returns_wp_error_if_no_key_set() {
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$error = $service->validate_recovery_mode_key( 'abcd', HOUR_IN_SECONDS );
|
||||
$error = $service->validate_recovery_mode_key( '', 'abcd', HOUR_IN_SECONDS );
|
||||
|
||||
$this->assertWPError( $error );
|
||||
$this->assertEquals( 'no_recovery_key_set', $error->get_error_code() );
|
||||
$this->assertEquals( 'token_not_found', $error->get_error_code() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 46130
|
||||
*/
|
||||
public function test_validate_recovery_mode_key_returns_wp_error_if_stored_format_is_invalid() {
|
||||
update_option( 'recovery_key', 'gibberish' );
|
||||
public function test_validate_recovery_mode_key_returns_wp_error_if_data_missing() {
|
||||
update_option( 'recovery_keys', 'gibberish' );
|
||||
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$error = $service->validate_recovery_mode_key( 'abcd', HOUR_IN_SECONDS );
|
||||
$error = $service->validate_recovery_mode_key( '', 'abcd', HOUR_IN_SECONDS );
|
||||
|
||||
$this->assertWPError( $error );
|
||||
$this->assertEquals( 'token_not_found', $error->get_error_code() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 46130
|
||||
*/
|
||||
public function test_validate_recovery_mode_key_returns_wp_error_if_bad() {
|
||||
update_option( 'recovery_keys', array( 'token' => 'gibberish' ) );
|
||||
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$error = $service->validate_recovery_mode_key( 'token', 'abcd', HOUR_IN_SECONDS );
|
||||
|
||||
$this->assertWPError( $error );
|
||||
$this->assertEquals( 'invalid_recovery_key_format', $error->get_error_code() );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ticket 46130
|
||||
*/
|
||||
public function test_validate_recovery_mode_key_returns_wp_error_if_stored_format_is_invalid() {
|
||||
|
||||
$token = wp_generate_password( 22, false );
|
||||
update_option( 'recovery_keys', array( $token => 'gibberish' ) );
|
||||
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$error = $service->validate_recovery_mode_key( $token, 'abcd', HOUR_IN_SECONDS );
|
||||
|
||||
$this->assertWPError( $error );
|
||||
$this->assertEquals( 'invalid_recovery_key_format', $error->get_error_code() );
|
||||
@@ -44,8 +74,9 @@ class Tests_Recovery_Mode_Key_Service extends WP_UnitTestCase {
|
||||
*/
|
||||
public function test_validate_recovery_mode_key_returns_wp_error_if_empty_key() {
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$service->generate_and_store_recovery_mode_key();
|
||||
$error = $service->validate_recovery_mode_key( '', HOUR_IN_SECONDS );
|
||||
$token = $service->generate_recovery_mode_token();
|
||||
$service->generate_and_store_recovery_mode_key( $token );
|
||||
$error = $service->validate_recovery_mode_key( $token, '', HOUR_IN_SECONDS );
|
||||
|
||||
$this->assertWPError( $error );
|
||||
$this->assertEquals( 'hash_mismatch', $error->get_error_code() );
|
||||
@@ -56,8 +87,9 @@ class Tests_Recovery_Mode_Key_Service extends WP_UnitTestCase {
|
||||
*/
|
||||
public function test_validate_recovery_mode_key_returns_wp_error_if_hash_mismatch() {
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$service->generate_and_store_recovery_mode_key();
|
||||
$error = $service->validate_recovery_mode_key( 'abcd', HOUR_IN_SECONDS );
|
||||
$token = $service->generate_recovery_mode_token();
|
||||
$service->generate_and_store_recovery_mode_key( $token );
|
||||
$error = $service->validate_recovery_mode_key( $token, 'abcd', HOUR_IN_SECONDS );
|
||||
|
||||
$this->assertWPError( $error );
|
||||
$this->assertEquals( 'hash_mismatch', $error->get_error_code() );
|
||||
@@ -68,13 +100,14 @@ class Tests_Recovery_Mode_Key_Service extends WP_UnitTestCase {
|
||||
*/
|
||||
public function test_validate_recovery_mode_key_returns_wp_error_if_expired() {
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$key = $service->generate_and_store_recovery_mode_key();
|
||||
$token = $service->generate_recovery_mode_token();
|
||||
$key = $service->generate_and_store_recovery_mode_key( $token );
|
||||
|
||||
$record = get_option( 'recovery_key' );
|
||||
$record['created_at'] = time() - HOUR_IN_SECONDS - 30;
|
||||
update_option( 'recovery_key', $record );
|
||||
$records = get_option( 'recovery_keys' );
|
||||
$records[ $token ]['created_at'] = time() - HOUR_IN_SECONDS - 30;
|
||||
update_option( 'recovery_keys', $records );
|
||||
|
||||
$error = $service->validate_recovery_mode_key( $key, HOUR_IN_SECONDS );
|
||||
$error = $service->validate_recovery_mode_key( $token, $key, HOUR_IN_SECONDS );
|
||||
|
||||
$this->assertWPError( $error );
|
||||
$this->assertEquals( 'key_expired', $error->get_error_code() );
|
||||
@@ -85,7 +118,66 @@ class Tests_Recovery_Mode_Key_Service extends WP_UnitTestCase {
|
||||
*/
|
||||
public function test_validate_recovery_mode_key_returns_true_for_valid_key() {
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$key = $service->generate_and_store_recovery_mode_key();
|
||||
$this->assertTrue( $service->validate_recovery_mode_key( $key, HOUR_IN_SECONDS ) );
|
||||
$token = $service->generate_recovery_mode_token();
|
||||
$key = $service->generate_and_store_recovery_mode_key( $token );
|
||||
$this->assertTrue( $service->validate_recovery_mode_key( $token, $key, HOUR_IN_SECONDS ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 46595
|
||||
*/
|
||||
public function test_validate_recovery_mode_key_returns_error_if_token_used_more_than_once() {
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$token = $service->generate_recovery_mode_token();
|
||||
$key = $service->generate_and_store_recovery_mode_key( $token );
|
||||
|
||||
$this->assertTrue( $service->validate_recovery_mode_key( $token, $key, HOUR_IN_SECONDS ) );
|
||||
|
||||
// data should be remove by first call
|
||||
$error = $service->validate_recovery_mode_key( $token, $key, HOUR_IN_SECONDS );
|
||||
|
||||
$this->assertWPError( $error );
|
||||
$this->assertEquals( 'token_not_found', $error->get_error_code() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 46595
|
||||
*/
|
||||
public function test_validate_recovery_mode_key_returns_error_if_token_used_more_than_once_more_than_key_stored() {
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
|
||||
// create an extra key
|
||||
$token = $service->generate_recovery_mode_token();
|
||||
$service->generate_and_store_recovery_mode_key( $token );
|
||||
|
||||
$token = $service->generate_recovery_mode_token();
|
||||
$key = $service->generate_and_store_recovery_mode_key( $token );
|
||||
|
||||
$this->assertTrue( $service->validate_recovery_mode_key( $token, $key, HOUR_IN_SECONDS ) );
|
||||
|
||||
// data should be remove by first call
|
||||
$error = $service->validate_recovery_mode_key( $token, $key, HOUR_IN_SECONDS );
|
||||
|
||||
$this->assertWPError( $error );
|
||||
$this->assertEquals( 'token_not_found', $error->get_error_code() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 46595
|
||||
*/
|
||||
public function test_clean_expired_keys() {
|
||||
$service = new WP_Recovery_Mode_Key_Service();
|
||||
$token = $service->generate_recovery_mode_token();
|
||||
$service->generate_and_store_recovery_mode_key( $token );
|
||||
|
||||
$records = get_option( 'recovery_keys' );
|
||||
|
||||
$records[ $token ]['created_at'] = time() - HOUR_IN_SECONDS - 30;
|
||||
|
||||
update_option( 'recovery_keys', $records );
|
||||
|
||||
$service->clean_expired_keys( HOUR_IN_SECONDS );
|
||||
|
||||
$this->assertEmpty( get_option( 'recovery_keys' ) );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user