123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839 |
- <?php
- /**
- * Testing Ajax handler for erasing personal data.
- *
- * @package WordPress\UnitTests
- * @since 5.2.0
- */
- /**
- * Tests_Ajax_PrivacyExportPersonalData class.
- *
- * @since 5.2.0
- *
- * @group ajax
- * @group privacy
- *
- * @covers ::wp_ajax_wp_privacy_erase_personal_data
- */
- class Tests_Ajax_PrivacyErasePersonalData extends WP_Ajax_UnitTestCase {
- /**
- * User Request ID.
- *
- * @since 5.2.0
- *
- * @var int $request_id
- */
- protected static $request_id;
- /**
- * User Request Email.
- *
- * @since 5.2.0
- *
- * @var string $request_email
- */
- protected static $request_email;
- /**
- * Ajax Action.
- *
- * @since 5.2.0
- *
- * @var string $action
- */
- protected static $action;
- /**
- * Eraser Index.
- *
- * @since 5.2.0
- *
- * @var int $eraser
- */
- protected static $eraser;
- /**
- * Eraser Key.
- *
- * @since 5.2.0
- *
- * @var string $eraser_key
- */
- protected static $eraser_key;
- /**
- * Eraser Friendly Name.
- *
- * @since 5.2.0
- *
- * @var string $eraser_friendly_name
- */
- protected static $eraser_friendly_name;
- /**
- * Page Index.
- *
- * @since 5.2.0
- *
- * @var int $page
- */
- protected static $page;
- /**
- * Last response parsed.
- *
- * @since 5.2.0
- *
- * @var array $_last_response_parsed
- */
- protected $_last_response_parsed;
- /**
- * An array key in the test eraser to unset.
- *
- * @since 5.2.0
- *
- * @var string $key_to_unset
- */
- protected $key_to_unset;
- /**
- * A value to change the test eraser callback to.
- *
- * @since 5.2.0
- *
- * @var string $new_callback_value
- */
- protected $new_callback_value;
- /**
- * Create user erase request fixtures.
- *
- * @param WP_UnitTest_Factory $factory Factory.
- */
- public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
- self::$request_email = 'requester@example.com';
- self::$request_id = wp_create_user_request( self::$request_email, 'remove_personal_data' );
- self::$action = 'wp-privacy-erase-personal-data';
- self::$eraser = 1;
- self::$eraser_key = 'custom-eraser';
- self::$eraser_friendly_name = 'Custom Eraser';
- self::$page = 1;
- }
- /**
- * Register a custom personal data eraser.
- */
- public function setUp() {
- parent::setUp();
- $this->key_to_unset = '';
- // Make sure the erasers response is not modified and avoid sending emails.
- remove_all_filters( 'wp_privacy_personal_data_erasure_page' );
- remove_all_actions( 'wp_privacy_personal_data_erased' );
- // Only use our custom privacy personal data eraser.
- remove_all_filters( 'wp_privacy_personal_data_erasers' );
- add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) );
- $this->_setRole( 'administrator' );
- // `erase_others_personal_data` meta cap in Multisite installation is only granted to those with `manage_network` capability.
- if ( is_multisite() ) {
- grant_super_admin( get_current_user_id() );
- }
- }
- /**
- * Clean up after each test method.
- */
- public function tearDown() {
- remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) );
- $this->new_callback_value = '';
- if ( is_multisite() ) {
- revoke_super_admin( get_current_user_id() );
- }
- parent::tearDown();
- }
- /**
- * Helper method for changing the test eraser's callback function.
- *
- * @param string|array $callback New test eraser callback index value.
- */
- protected function _set_eraser_callback( $callback ) {
- $this->new_callback_value = $callback;
- add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 );
- }
- /**
- * Change the test eraser callback to a specified value.
- *
- * @since 5.2.0
- *
- * @param array $erasers List of data erasers.
- *
- * @return array Array of data erasers.
- */
- public function filter_eraser_callback_value( $erasers ) {
- $erasers[ self::$eraser_key ]['callback'] = $this->new_callback_value;
- return $erasers;
- }
- /**
- * Helper method for unsetting an array index in the test eraser.
- *
- * @param string|bool $key Test eraser key to unset.
- */
- protected function _unset_eraser_key( $key ) {
- $this->key_to_unset = $key;
- add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 );
- }
- /**
- * Unsets an array key in the test eraser.
- *
- * If the key is false, the eraser is set to false.
- *
- * @since 5.2.0
- *
- * @param array $erasers Erasers.
- *
- * @return array Erasers.
- */
- public function filter_unset_eraser_index( $erasers ) {
- if ( false === $this->key_to_unset ) {
- $erasers[ self::$eraser_key ] = false;
- } elseif ( ! empty( $this->key_to_unset ) ) {
- unset( $erasers[ self::$eraser_key ][ $this->key_to_unset ] );
- }
- return $erasers;
- }
- /**
- * Helper method for erasing a key from the eraser response.
- *
- * @since 5.2.0
- *
- * @param array $key Response key to unset.
- */
- protected function _unset_response_key( $key ) {
- $this->key_to_unset = $key;
- $this->_set_eraser_callback( array( $this, 'filter_unset_response_index' ) );
- }
- /**
- * Unsets an array index in a response.
- *
- * @since 5.2.0
- *
- * @param string $email_address The requester's email address.
- * @param int $page Page number.
- *
- * @return array Export data.
- */
- public function filter_unset_response_index( $email_address, $page = 1 ) {
- $response = $this->callback_personal_data_eraser( $email_address, $page );
- if ( ! empty( $this->key_to_unset ) ) {
- unset( $response[ $this->key_to_unset ] );
- }
- return $response;
- }
- /**
- * The function should send an error when the request ID is missing.
- *
- * @since 5.2.0
- *
- * @ticket 43438
- */
- public function test_error_when_missing_request_id() {
- $this->assertNotWPError( self::$request_id );
- // Set up a request.
- $this->_make_ajax_call(
- array(
- 'id' => null, // Missing request ID.
- )
- );
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] );
- }
- /**
- * The function should send an error when the request ID is less than 1.
- *
- * @since 5.2.0
- *
- * @ticket 43438
- */
- public function test_error_when_request_id_invalid() {
- $this->assertNotWPError( self::$request_id );
- // Set up a request.
- $this->_make_ajax_call(
- array(
- 'id' => -1, // Invalid request ID.
- )
- );
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] );
- }
- /**
- * The function should send an error when the current user is missing required capabilities.
- *
- * @since 5.2.0
- *
- * @ticket 43438
- */
- public function test_error_when_current_user_missing_required_capabilities() {
- $this->_setRole( 'author' );
- $this->assertFalse( current_user_can( 'erase_others_personal_data' ) );
- $this->assertFalse( current_user_can( 'delete_users' ) );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
- }
- /**
- * Test requests do not succeed on multisite when the current user is not a network admin.
- *
- * @ticket 43438
- * @group multisite
- * @group ms-required
- */
- public function test_error_when_current_user_missing_required_capabilities_multisite() {
- revoke_super_admin( get_current_user_id() );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
- }
- /**
- * The function should send an error when the nonce does not validate.
- *
- * @since 5.2.0
- */
- public function test_failure_with_invalid_nonce() {
- $this->expectException( 'WPAjaxDieStopException' );
- $this->expectExceptionMessage( '-1' );
- $this->_make_ajax_call(
- array(
- 'security' => 'invalid-nonce',
- )
- );
- }
- /**
- * The function should send an error when the request type is incorrect.
- *
- * @since 5.2.0
- */
- public function test_error_when_incorrect_request_type() {
- $request_id = wp_create_user_request(
- 'export-request@example.com',
- 'export_personal_data' // Incorrect request type, expects 'remove_personal_data'.
- );
- $this->_make_ajax_call(
- array(
- 'security' => wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ),
- 'id' => $request_id,
- )
- );
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] );
- }
- /**
- * The function should send an error when the request email is invalid.
- *
- * @since 5.2.0
- */
- public function test_error_when_invalid_email() {
- wp_update_post(
- array(
- 'ID' => self::$request_id,
- 'post_title' => '', // Invalid requester's email address.
- )
- );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Invalid email address in request.', $this->_last_response_parsed['data'] );
- }
- /**
- * The function should send an error when the eraser index is missing.
- *
- * @since 5.2.0
- */
- public function test_error_when_missing_eraser_index() {
- $this->_make_ajax_call(
- array(
- 'eraser' => null, // Missing eraser index.
- )
- );
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Missing eraser index.', $this->_last_response_parsed['data'] );
- }
- /**
- * The function should send an error when the page index is missing.
- *
- * @since 5.2.0
- */
- public function test_error_when_missing_page_index() {
- $this->_make_ajax_call(
- array(
- 'page' => null, // Missing page index.
- )
- );
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] );
- }
- /**
- * The function should send an error when the eraser index is negative.
- *
- * @since 5.2.0
- */
- public function test_error_when_negative_eraser_index() {
- $this->_make_ajax_call(
- array(
- 'eraser' => -1, // Negative eraser index.
- )
- );
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Eraser index cannot be less than one.', $this->_last_response_parsed['data'] );
- }
- /**
- * The function should send an error when the eraser index is out of range.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_index_out_of_range() {
- $this->_make_ajax_call(
- array(
- 'eraser' => PHP_INT_MAX, // Out of range eraser index.
- )
- );
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Eraser index is out of range.', $this->_last_response_parsed['data'] );
- }
- /**
- * The function should send an error when the page index is less than one.
- *
- * @since 5.2.0
- */
- public function test_error_when_page_index_less_than_one() {
- $this->_make_ajax_call(
- array(
- 'page' => 0, // Page index less than one.
- )
- );
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] );
- }
- /**
- * The function should send an error when an eraser is not an array.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_not_array() {
- $this->_unset_eraser_key( false );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame(
- sprintf(
- 'Expected an array describing the eraser at index %s.',
- self::$eraser
- ),
- $this->_last_response_parsed['data']
- );
- }
- /**
- * The function should send an error when an eraser is missing a friendly name.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_missing_friendly_name() {
- $this->_unset_eraser_key( 'eraser_friendly_name' );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame(
- sprintf(
- 'Eraser array at index %s does not include a friendly name.',
- self::$eraser
- ),
- $this->_last_response_parsed['data']
- );
- }
- /**
- * The function should send an error when an eraser is missing a callback.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_missing_callback() {
- $this->_unset_eraser_key( 'callback' );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame(
- sprintf(
- 'Eraser does not include a callback: %s.',
- self::$eraser_friendly_name
- ),
- $this->_last_response_parsed['data']
- );
- }
- /**
- * The function should send an error when an eraser, at a given index, has an invalid callback.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_index_invalid_callback() {
- $this->_set_eraser_callback( false );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame(
- sprintf(
- 'Eraser callback is not valid: %s.',
- self::$eraser_friendly_name
- ),
- $this->_last_response_parsed['data']
- );
- }
- /**
- * The function should send an error when an eraser, at a given index, is missing an array response.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_index_invalid_response() {
- $this->_set_eraser_callback( '__return_null' );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame(
- sprintf(
- 'Did not receive array from %1$s eraser (index %2$d).',
- self::$eraser_friendly_name,
- self::$eraser
- ),
- $this->_last_response_parsed['data']
- );
- }
- /**
- * The function should send an error when missing an items_removed index.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_items_removed_missing() {
- $this->_unset_response_key( 'items_removed' );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame(
- sprintf(
- 'Expected items_removed key in response array from %1$s eraser (index %2$d).',
- self::$eraser_friendly_name,
- self::$eraser
- ),
- $this->_last_response_parsed['data']
- );
- }
- /**
- * The function should send an error when missing an items_retained index.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_items_retained_missing() {
- $this->_unset_response_key( 'items_retained' );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame(
- sprintf(
- 'Expected items_retained key in response array from %1$s eraser (index %2$d).',
- self::$eraser_friendly_name,
- self::$eraser
- ),
- $this->_last_response_parsed['data']
- );
- }
- /**
- * The function should send an error when missing a messages index.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_messages_missing() {
- $this->_unset_response_key( 'messages' );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame(
- sprintf(
- 'Expected messages key in response array from %1$s eraser (index %2$d).',
- self::$eraser_friendly_name,
- self::$eraser
- ),
- $this->_last_response_parsed['data']
- );
- }
- /**
- * The function should send an error when the messages index is not an array.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_messages_not_array() {
- $this->_set_eraser_callback( array( $this, 'filter_response_messages_invalid' ) );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame(
- sprintf(
- 'Expected messages key to reference an array in response array from %1$s eraser (index %2$d).',
- self::$eraser_friendly_name,
- self::$eraser
- ),
- $this->_last_response_parsed['data']
- );
- }
- /**
- * Change the messages index to an invalid value (not an array).
- *
- * @since 5.2.0
- *
- * @param string $email_address The requester's email address.
- * @param int $page Page number.
- *
- * @return array Export data.
- */
- public function filter_response_messages_invalid( $email_address, $page = 1 ) {
- $response = $this->callback_personal_data_eraser( $email_address, $page );
- $response['messages'] = true;
- return $response;
- }
- /**
- * The function should send an error when an eraser is missing 'done' in array response.
- *
- * @since 5.2.0
- */
- public function test_error_when_eraser_missing_done_response() {
- $this->_unset_response_key( 'done' );
- $this->_make_ajax_call();
- $this->assertFalse( $this->_last_response_parsed['success'] );
- $this->assertSame(
- sprintf(
- 'Expected done flag in response array from %1$s eraser (index %2$d).',
- self::$eraser_friendly_name,
- self::$eraser
- ),
- $this->_last_response_parsed['data']
- );
- }
- /**
- * The function should successfully send erasers response data when the current user has the required
- * capabilities.
- *
- * @since 5.2.0
- *
- * @ticket 43438
- */
- public function test_success_when_current_user_has_required_capabilities() {
- $this->assertTrue( current_user_can( 'erase_others_personal_data' ) );
- $this->assertTrue( current_user_can( 'delete_users' ) );
- $this->_make_ajax_call();
- $this->assertSame(
- sprintf( 'A message regarding retained data for %s.', self::$request_email ),
- $this->_last_response_parsed['data']['messages'][0]
- );
- $this->assertTrue( $this->_last_response_parsed['success'] );
- $this->assertTrue( $this->_last_response_parsed['data']['items_removed'] );
- $this->assertTrue( $this->_last_response_parsed['data']['items_retained'] );
- $this->assertTrue( $this->_last_response_parsed['data']['done'] );
- }
- /**
- * The function should successfully send erasers response data when no items to erase.
- *
- * @since 5.2.0
- *
- * @ticket 43438
- */
- public function test_success_when_no_items_to_erase() {
- $this->_make_ajax_call( array( 'page' => 2 ) );
- $this->assertTrue( $this->_last_response_parsed['success'] );
- $this->assertFalse( $this->_last_response_parsed['data']['items_removed'] );
- $this->assertFalse( $this->_last_response_parsed['data']['items_retained'] );
- $this->assertEmpty( $this->_last_response_parsed['data']['messages'] );
- $this->assertTrue( $this->_last_response_parsed['data']['done'] );
- }
- /**
- * Test that the function's output should be filterable with the `wp_privacy_personal_data_erasure_page` filter.
- *
- * @since 5.2.0
- */
- public function test_output_should_be_filterable() {
- add_filter( 'wp_privacy_personal_data_erasure_page', array( $this, 'filter_eraser_data_response' ), 20, 6 );
- $this->_make_ajax_call();
- $expected_new_index = self::$request_email . '-' . self::$request_id . '-' . self::$eraser_key;
- $this->assertTrue( $this->_last_response_parsed['success'] );
- $this->assertSame( 'filtered removed', $this->_last_response_parsed['data']['items_removed'] );
- $this->assertSame( 'filtered retained', $this->_last_response_parsed['data']['items_retained'] );
- $this->assertSame( array( 'filtered messages' ), $this->_last_response_parsed['data']['messages'] );
- $this->assertSame( 'filtered done', $this->_last_response_parsed['data']['done'] );
- $this->assertSame( $expected_new_index, $this->_last_response_parsed['data']['new_index'] );
- }
- /**
- * Filters the eraser response.
- *
- * @since 5.2.0
- *
- * @param array $response The personal data for the given eraser and page.
- * @param int $eraser_index The index of the eraser that provided this data.
- * @param string $email_address The email address associated with this personal data.
- * @param int $page The page for this response.
- * @param int $request_id The privacy request post ID associated with this request.
- * @param string $eraser_key The key (slug) of the eraser that provided this data.
- *
- * @return array Filtered erase response.
- */
- public function filter_eraser_data_response( $response, $eraser_index, $email_address, $page, $request_id, $eraser_key ) {
- $response['items_removed'] = 'filtered removed';
- $response['items_retained'] = 'filtered retained';
- $response['messages'] = array( 'filtered messages' );
- $response['done'] = 'filtered done';
- $response['new_index'] = $email_address . '-' . $request_id . '-' . $eraser_key;
- return $response;
- }
- /**
- * Register handler for a custom personal data eraser.
- *
- * @since 5.2.0
- *
- * @param array $erasers An array of personal data erasers.
- *
- * @return array An array of personal data erasers.
- */
- public function register_custom_personal_data_eraser( $erasers ) {
- $erasers[ self::$eraser_key ] = array(
- 'eraser_friendly_name' => self::$eraser_friendly_name,
- 'callback' => array( $this, 'callback_personal_data_eraser' ),
- );
- return $erasers;
- }
- /**
- * Custom Personal Data Eraser.
- *
- * @since 5.2.0
- *
- * @param string $email_address The comment author email address.
- * @param int $page Page number.
- *
- * @return array Erase data.
- */
- public function callback_personal_data_eraser( $email_address, $page = 1 ) {
- if ( 1 === $page ) {
- return array(
- 'items_removed' => true,
- 'items_retained' => true,
- 'messages' => array( sprintf( 'A message regarding retained data for %s.', $email_address ) ),
- 'done' => true,
- );
- }
- return array(
- 'items_removed' => false,
- 'items_retained' => false,
- 'messages' => array(),
- 'done' => true,
- );
- }
- /**
- * Helper function for Ajax handler.
- *
- * @since 5.2.0
- *
- * @param array $args Ajax request arguments.
- */
- protected function _make_ajax_call( $args = array() ) {
- $this->_last_response_parsed = null;
- $this->_last_response = '';
- $defaults = array(
- 'action' => self::$action,
- 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ),
- 'page' => self::$page,
- 'id' => self::$request_id,
- 'eraser' => self::$eraser,
- );
- $_POST = wp_parse_args( $args, $defaults );
- try {
- $this->_handleAjax( self::$action );
- } catch ( WPAjaxDieContinueException $e ) {
- unset( $e );
- }
- if ( $this->_last_response ) {
- $this->_last_response_parsed = json_decode( $this->_last_response, true );
- }
- }
- }
|