Privacy: Allow new requests to be created after a user’s existing one has been completed.

When dealing with personal data exports and erasure requests, it is important to have a log of all the requests for a specific person. This is often required to confirm when and how many times requests were completed and fulfilled properly. 

This change allows a new request to be created after a previous data request has reached completed status (`request-completed`) instead of requiring admins to delete or re-initiate the existing request. The latter approach removes the historical log of requests for that user when creating a new request.

Full unit tests for the `wp_create_user_request()` function are also included.

Props garrett-eclipse, cc0a, birgire, desrosj.
Fixes #44707.

git-svn-id: https://develop.svn.wordpress.org/trunk@44906 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Jonathan Desrosiers 2019-03-15 16:26:55 +00:00
parent 28714c42bd
commit b66ae2b4c1
2 changed files with 303 additions and 2 deletions

View File

@ -3330,13 +3330,16 @@ function wp_create_user_request( $email_address = '', $action_name = '', $reques
'post_type' => 'user_request',
'post_name__in' => array( $action_name ), // Action name stored in post_name column.
'title' => $email_address, // Email address stored in post_title column.
'post_status' => 'any',
'post_status' => array(
'request-pending',
'request-confirmed',
),
'fields' => 'ids',
)
);
if ( $requests_query->found_posts ) {
return new WP_Error( 'duplicate_request', __( 'A request for this email address already exists.' ) );
return new WP_Error( 'duplicate_request', __( 'An incomplete request for this email address already exists.' ) );
}
$request_id = wp_insert_post(

View File

@ -0,0 +1,298 @@
<?php
/**
* Test the `wp_create_user_request()` function.
*
* @package WordPress
* @subpackage UnitTests
* @since 5.2.0
*/
/**
* Tests_WpCreateUserRequest class.
*
* @group privacy
* @covers ::wp_create_user_request
*
* @since 5.2.0
*/
class Tests_WpCreateUserRequest extends WP_UnitTestCase {
/**
* Request ID.
*
* @since 5.2.0
*
* @var int $request_id
*/
protected static $request_id;
/**
* Request email for a registered user.
*
* @since 5.2.0
*
* @var string $registered_user_email
*/
protected static $registered_user_email;
/**
* Request email for a non-registered user.
*
* @since 5.2.0
*
* @var string $non_registered_user_email
*/
protected static $non_registered_user_email;
/**
* Test user ID.
*
* @since 5.2.0
*
* @var string $user_id
*/
protected static $user_id;
/**
* Create fixtures.
*
* @since 5.2.0
*
* @param WP_UnitTest_Factory $factory Factory.
*/
public static function wpSetUpBeforeClass( $factory ) {
self::$registered_user_email = 'export@local.test';
self::$non_registered_user_email = 'non-registered-user@local.test';
self::$user_id = $factory->user->create(
array(
'user_email' => self::$registered_user_email,
)
);
self::$request_id = $factory->post->create(
array(
'post_type' => 'user_request',
'post_author' => self::$user_id,
'post_name' => 'export_personal_data',
'post_status' => 'request-pending',
'post_title' => self::$registered_user_email,
)
);
}
/**
* Ensure a WP_Error is returned when an invalid email is passed.
*
* @ticket 44707
*/
public function test_invalid_email() {
$actual = wp_create_user_request( 'not-a-valid-email', 'export_personal_data' );
$this->assertWPError( $actual );
$this->assertSame( 'invalid_email', $actual->get_error_code() );
}
/**
* Ensure a WP_Error is returned when an invalid action is passed.
*
* @ticket 44707
*/
public function test_invalid_action() {
$actual = wp_create_user_request( self::$registered_user_email, false );
$this->assertWPError( $actual );
$this->assertSame( 'invalid_action', $actual->get_error_code() );
}
/**
* When there are incomplete requests for a registered user, a WP_Error should be returned.
*
* @ticket 44707
*/
public function test_failure_due_to_incomplete_registered_user() {
// Second request (duplicated).
$actual = wp_create_user_request( self::$registered_user_email, 'export_personal_data' );
$this->assertWPError( $actual );
$this->assertSame( 'duplicate_request', $actual->get_error_code() );
}
/**
* When there are incomplete requests for an non-registered user, a WP_Error should be returned.
*
* @ticket 44707
*/
public function test_failure_due_to_incomplete_unregistered_user() {
// Update first request.
wp_update_post(
array(
'ID' => self::$request_id,
'post_author' => 0,
'post_title' => self::$non_registered_user_email,
)
);
// Second request (duplicated).
$actual = wp_create_user_request( self::$non_registered_user_email, 'export_personal_data' );
$this->assertWPError( $actual );
$this->assertSame( 'duplicate_request', $actual->get_error_code() );
}
/**
* Ensure emails are properly sanitized.
*
* @ticket 44707
*/
public function test_sanitized_email() {
$actual = wp_create_user_request( 'some(email<withinvalid\characters@local.test', 'export_personal_data' );
$this->assertNotWPError( $actual );
$post = get_post( $actual );
$this->assertSame( 'export_personal_data', $post->post_name );
$this->assertSame( 'someemailwithinvalidcharacters@local.test', $post->post_title );
}
/**
* Ensure action names are properly sanitized.
*
* @ticket 44707
*/
public function test_sanitized_action_name() {
$actual = wp_create_user_request( self::$non_registered_user_email, 'some[custom*action\name' );
$this->assertNotWPError( $actual );
$post = get_post( $actual );
$this->assertSame( 'somecustomactionname', $post->post_name );
$this->assertSame( self::$non_registered_user_email, $post->post_title );
}
/**
* Test a user request is created successfully for a registered user.
*
* @ticket 44707
*/
public function test_create_request_registered_user() {
wp_delete_post( self::$request_id, true );
$test_data = array(
'test-data' => 'test value here',
'test index' => 'more privacy data',
);
$actual = wp_create_user_request( self::$registered_user_email, 'export_personal_data', $test_data );
$this->assertNotWPError( $actual );
$post = get_post( $actual );
$this->assertSame( self::$user_id, (int) $post->post_author );
$this->assertSame( 'export_personal_data', $post->post_name );
$this->assertSame( self::$registered_user_email, $post->post_title );
$this->assertSame( 'request-pending', $post->post_status );
$this->assertSame( 'user_request', $post->post_type );
$this->assertSame( wp_json_encode( $test_data ), $post->post_content );
}
/**
* Test a user request is created successfully for an non-registered user.
*
* @ticket 44707
*/
public function test_create_request_unregistered_user() {
wp_delete_post( self::$request_id, true );
$test_data = array(
'test-data' => 'test value here',
'test index' => 'more privacy data',
);
$actual = wp_create_user_request( self::$non_registered_user_email, 'export_personal_data', $test_data );
$this->assertNotWPError( $actual );
$post = get_post( $actual );
$this->assertSame( 0, (int) $post->post_author );
$this->assertSame( 'export_personal_data', $post->post_name );
$this->assertSame( self::$non_registered_user_email, $post->post_title );
$this->assertSame( 'request-pending', $post->post_status );
$this->assertSame( 'user_request', $post->post_type );
$this->assertSame( wp_json_encode( $test_data ), $post->post_content );
}
/**
* Test that a pre-existing request for the same registered user that is not pending or confirmed status does not
* block a new request.
*
* @ticket 44707
*/
public function test_completed_request_does_not_block_new_request() {
// Update first request.
wp_update_post(
array(
'ID' => self::$request_id,
'post_status' => 'request-completed', // Not 'request-pending' or 'request-confirmed'.
)
);
// Second request.
$actual = wp_create_user_request( self::$registered_user_email, 'export_personal_data' );
$this->assertNotWPError( $actual );
$post = get_post( $actual );
$this->assertSame( self::$registered_user_email, $post->post_title );
$this->assertSame( 'request-pending', $post->post_status );
$this->assertSame( 'user_request', $post->post_type );
}
/**
* Test that a pre-existing request for the same non-registered user that is not pending or confirmed status does not
* block a new request.
*
* @ticket 44707
*/
public function test_completed_request_does_not_block_new_request_for_unregistered_user() {
wp_update_post(
array(
'ID' => self::$request_id,
'post_author' => 0,
'post_title' => self::$non_registered_user_email,
'post_status' => 'request-failed', // Not 'request-pending' or 'request-confirmed'.
)
);
$actual = wp_create_user_request( self::$non_registered_user_email, 'export_personal_data' );
$this->assertNotWPError( $actual );
$post = get_post( $actual );
$this->assertSame( 0, (int) $post->post_author );
$this->assertSame( 'export_personal_data', $post->post_name );
$this->assertSame( self::$non_registered_user_email, $post->post_title );
$this->assertSame( 'request-pending', $post->post_status );
$this->assertSame( 'user_request', $post->post_type );
}
/**
* Test that an error from `wp_insert_post()` is returned.
*
* @ticket 44707
*/
public function test_wp_error_returned_from_wp_insert_post() {
wp_delete_post( self::$request_id, true );
add_filter( 'wp_insert_post_empty_content', '__return_true' );
$actual = wp_create_user_request( self::$registered_user_email, 'export_personal_data' );
$this->assertWPError( $actual );
$this->assertSame( 'empty_content', $actual->get_error_code() );
}
}