Completed
Push — update/backup-pkg-real-time-ba... ( 182945...8e36b9 )
by
unknown
40:09 queued 29:02
created

test_backup_options_unauthorized()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14

Duplication

Lines 14
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 14
loc 14
rs 9.7998
c 0
b 0
f 0
1
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3
namespace Automattic\Jetpack\Backup;
4
5
use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
6
use PHPUnit\Framework\TestCase;
7
use WorDBless\Options as WorDBless_Options;
8
use WorDBless\Posts as WorDBless_Posts;
9
use WorDBless\Users as WorDBless_Users;
10
use WP_REST_Request;
11
use WP_REST_Server;
12
13
/**
14
 * Unit tests for the REST_Controller class.
15
 *
16
 * @package automattic/jetpack-backup
17
 */
18
class Test_REST_Controller extends TestCase {
19
20
	/**
21
	 * REST Server object.
22
	 *
23
	 * @var WP_REST_Server
24
	 */
25
	private $server;
26
27
	/**
28
	 * Setting up the test.
29
	 *
30
	 * @before
31
	 */
32
	public function set_up() {
33
		global $wp_rest_server;
34
35
		$wp_rest_server = new WP_REST_Server();
36
		$this->server   = $wp_rest_server;
37
		$this->admin_id = wp_insert_user(
38
			array(
39
				'user_login' => 'dummy_user',
40
				'user_pass'  => 'dummy_pass',
41
				'role'       => 'administrator',
42
			)
43
		);
44
		wp_set_current_user( 0 );
45
46
		// Register REST routes.
47
		add_action( 'rest_api_init', array( 'Automattic\\Jetpack\\Backup\\REST_Controller', 'register_rest_routes' ) );
48
49
		do_action( 'rest_api_init' );
50
	}
51
52
	/**
53
	 * Returning the environment into its initial state.
54
	 *
55
	 * @after
56
	 */
57
	public function tear_down() {
58
		wp_set_current_user( 0 );
59
		WorDBless_Users::init()->clear_all_users();
60
		// phpcs:disable WordPress.Security.NonceVerification.Recommended
61
		unset(
62
			$_GET['_for'],
63
			$_GET['token'],
64
			$_GET['timestamp'],
65
			$_GET['nonce'],
66
			$_GET['body-hash'],
67
			$_GET['signature'],
68
			$_SERVER['REQUEST_METHOD']
69
		);
70
		// phpcs:enable WordPress.Security.NonceVerification.Recommended
71
72
		WorDBless_Options::init()->clear_options();
73
		WorDBless_Posts::init()->clear_all_posts();
74
		WorDBless_Users::init()->clear_all_users();
75
	}
76
77
	/**
78
	 * Testing the `POST /jetpack/v4/backup-helper-script` endpoint when the `helper` param is missing.
79
	 */
80 View Code Duplication
	public function test_install_backup_helper_script_missing_required_param() {
81
		$request  = new WP_REST_Request( 'POST', '/jetpack/v4/backup-helper-script' );
82
		$response = $this->server->dispatch( $request );
83
		$this->assertEquals( 400, $response->get_status() );
84
		$this->assertEquals( 'Missing parameter(s): helper', $response->get_data()['message'] );
85
	}
86
87
	/**
88
	 * Testing the `POST /jetpack/v4/backup-helper-script` endpoint with admin user.
89
	 */
90 View Code Duplication
	public function test_install_backup_helper_script_unauthorized() {
91
		wp_set_current_user( $this->admin_id );
92
93
		$body    = array(
94
			'helper' => 'dummy',
95
		);
96
		$request = new WP_REST_Request( 'POST', '/jetpack/v4/backup-helper-script' );
97
		$request->set_header( 'content-type', 'application/json' );
98
		$request->set_body( wp_json_encode( $body ) );
99
		$response = $this->server->dispatch( $request );
100
		$this->assertEquals( 403, $response->get_status() );
101
		$this->assertEquals( 'You are not allowed to perform this action.', $response->get_data()['message'] );
102
	}
103
104
	/**
105
	 * Testing the `POST /jetpack/v4/backup-helper-script` endpoint on success.
106
	 */
107
	public function test_install_backup_helper_script_success() {
108
		$body = array(
109
			// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
110
			'helper' => base64_encode( "<?php /* Jetpack Backup Helper Script */\n" ),
111
		);
112
113
		$request = new WP_REST_Request( 'POST', '/jetpack/v4/backup-helper-script' );
114
		$request->set_header( 'content-type', 'application/json' );
115
		$request->set_body( wp_json_encode( $body ) );
116
117
		$response = $this->dispatch_request_signed_with_blog_token( $request );
118
		$this->assertEquals( 200, $response->get_status() );
119
		$response_data = $response->get_data();
120
		$this->assertArrayHasKey( 'url', $response_data );
121
		$this->assertArrayHasKey( 'abspath', $response_data );
122
		$this->assertArrayHasKey( 'path', $response_data );
123
124
		// Cleanup.
125
		unlink( $response_data['path'] );
126
	}
127
128
	/**
129
	 * Testing the `DELETE /jetpack/v4/backup-helper-script` endpoint when the `path` param is missing.
130
	 */
131 View Code Duplication
	public function test_delete_backup_helper_script_missing_required_param() {
132
		$request  = new WP_REST_Request( 'DELETE', '/jetpack/v4/backup-helper-script' );
133
		$response = $this->server->dispatch( $request );
134
		$this->assertEquals( 400, $response->get_status() );
135
		$this->assertEquals( 'Missing parameter(s): path', $response->get_data()['message'] );
136
	}
137
138
	/**
139
	 * Testing the `DELETE /jetpack/v4/backup-helper-script` endpoint with admin user.
140
	 */
141 View Code Duplication
	public function test_delete_backup_helper_script_unauthorized() {
142
		wp_set_current_user( $this->admin_id );
143
144
		$body = array(
145
			'path' => 'dummy',
146
		);
147
148
		$request = new WP_REST_Request( 'DELETE', '/jetpack/v4/backup-helper-script' );
149
		$request->set_header( 'content-type', 'application/json' );
150
		$request->set_body( wp_json_encode( $body ) );
151
		$response = $this->server->dispatch( $request );
152
153
		$this->assertEquals( 403, $response->get_status() );
154
		$this->assertEquals( 'You are not allowed to perform this action.', $response->get_data()['message'] );
155
	}
156
157
	/**
158
	 * Testing the `DELETE /jetpack/v4/backup-helper-script` endpoint on success.
159
	 */
160 View Code Duplication
	public function test_delete_backup_helper_script_success() {
161
		$body = array(
162
			'path' => 'dummy',
163
		);
164
165
		$request = new WP_REST_Request( 'DELETE', '/jetpack/v4/backup-helper-script' );
166
		$request->set_header( 'content-type', 'application/json' );
167
		$request->set_body( wp_json_encode( $body ) );
168
169
		$response = $this->dispatch_request_signed_with_blog_token( $request );
170
		$this->assertEquals( 200, $response->get_status() );
171
172
		$this->assertTrue( $response->get_data()['success'] );
173
	}
174
175
	/**
176
	 * Testing the `/jetpack/v4/database-object/backup` endpoint with invalid params.
177
	 */
178
	public function test_backup_database_object_invalid_params() {
179
		$request  = new WP_REST_Request( 'GET', '/jetpack/v4/database-object/backup' );
180
		$response = $this->server->dispatch( $request );
181
182
		$this->assertEquals( 400, $response->get_status() );
183
		$this->assertEquals( 'Missing parameter(s): object_type, object_id', $response->get_data()['message'] );
184
185
		$request->set_query_params(
186
			array(
187
				'object_id'   => 123,
188
				'object_type' => 'dummy',
189
			)
190
		);
191
		$response      = $this->server->dispatch( $request );
192
		$response_data = $response->get_data();
193
		$this->assertEquals( 400, $response->get_status() );
194
		$this->assertEquals( 'Invalid parameter(s): object_type', $response_data['message'] );
195
		$this->assertEquals( 'The object_type argument should be one of woocommerce_attribute, woocommerce_downloadable_product_permission, woocommerce_order_item, woocommerce_payment_token, woocommerce_tax_rate, woocommerce_webhook', $response_data['data']['params']['object_type'] );
196
197
		$request->set_query_params(
198
			array(
199
				'object_id'   => 'should_be_integer',
200
				'object_type' => 'woocommerce_attribute',
201
			)
202
		);
203
		$response      = $this->server->dispatch( $request );
204
		$response_data = $response->get_data();
205
		$this->assertEquals( 400, $response->get_status() );
206
		$this->assertEquals( 'Invalid parameter(s): object_id', $response_data['message'] );
207
		$this->assertEquals( 'object_id is not of type integer.', $response_data['data']['params']['object_id'] );
208
	}
209
210
	/**
211
	 * Testing the `/jetpack/v4/database-object/backup` endpoint with admin user.
212
	 */
213 View Code Duplication
	public function test_backup_database_object_unauthorized() {
214
		wp_set_current_user( $this->admin_id );
215
216
		$request = new WP_REST_Request( 'GET', '/jetpack/v4/database-object/backup' );
217
		$request->set_query_params(
218
			array(
219
				'object_id'   => 123,
220
				'object_type' => 'woocommerce_attribute',
221
			)
222
		);
223
		$response = $this->server->dispatch( $request );
224
225
		$this->assertEquals( 403, $response->get_status() );
226
		$this->assertEquals( 'You are not allowed to perform this action.', $response->get_data()['message'] );
227
	}
228
229
	/**
230
	 * Testing the `/jetpack/v4/database-object/backup` endpoint on success.
231
	 */
232 View Code Duplication
	public function test_backup_database_object_success() {
233
		$request = new WP_REST_Request( 'GET', '/jetpack/v4/database-object/backup' );
234
		$request->set_query_params(
235
			array(
236
				'object_id'   => 123,
237
				'object_type' => 'woocommerce_attribute',
238
			)
239
		);
240
		$response = $this->server->dispatch( $request );
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
241
242
		$response = $this->dispatch_request_signed_with_blog_token( $request );
243
244
		$this->assertEquals( 404, $response->get_status() ); // success in this context.
245
246
		$this->assertEquals( 'Object not found', $response->get_data()['message'] );
247
	}
248
249
	/**
250
	 * Testing the `/jetpack/v4/options/backup` endpoint with invalid params.
251
	 */
252 View Code Duplication
	public function test_backup_options_invalid_params() {
253
		$request  = new WP_REST_Request( 'GET', '/jetpack/v4/options/backup' );
254
		$response = $this->server->dispatch( $request );
255
256
		$this->assertEquals( 400, $response->get_status() );
257
		$this->assertEquals( 'Missing parameter(s): name', $response->get_data()['message'] );
258
	}
259
260
	/**
261
	 * Testing the `/jetpack/v4/options/backup` endpoint with admin user.
262
	 */
263 View Code Duplication
	public function test_backup_options_unauthorized() {
264
		wp_set_current_user( $this->admin_id );
265
266
		$request = new WP_REST_Request( 'GET', '/jetpack/v4/options/backup' );
267
		$request->set_query_params(
268
			array(
269
				'name' => 'home',
270
			)
271
		);
272
		$response = $this->server->dispatch( $request );
273
274
		$this->assertEquals( 403, $response->get_status() );
275
		$this->assertEquals( 'You are not allowed to perform this action.', $response->get_data()['message'] );
276
	}
277
278
	/**
279
	 * Testing the `/jetpack/v4/options/backup` endpoint on success.
280
	 */
281 View Code Duplication
	public function test_backup_options_success() {
282
		$request = new WP_REST_Request( 'GET', '/jetpack/v4/options/backup' );
283
		$request->set_query_params(
284
			array(
285
				'name' => 'home',
286
			)
287
		);
288
		$response = $this->server->dispatch( $request );
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
289
290
		$response = $this->dispatch_request_signed_with_blog_token( $request );
291
292
		$this->assertEquals( 200, $response->get_status() );
293
	}
294
295
	/**
296
	 * Testing the `/jetpack/v4/comments/(?P<id>\d+)/backup` endpoint with admin user.
297
	 */
298
	public function test_backup_comment_unauthorized() {
299
		wp_set_current_user( $this->admin_id );
300
301
		$request  = new WP_REST_Request( 'GET', '/jetpack/v4/comments/1234/backup' );
302
		$response = $this->server->dispatch( $request );
303
304
		$this->assertEquals( 403, $response->get_status() );
305
		$this->assertEquals( 'You are not allowed to perform this action.', $response->get_data()['message'] );
306
	}
307
308
	/**
309
	 * Testing the `/jetpack/v4/comments/(?P<id>\d+)/backup` endpoint on success.
310
	 */
311 View Code Duplication
	public function test_backup_comment_success() {
312
		$request = new WP_REST_Request( 'GET', '/jetpack/v4/comments/1234/backup' );
313
314
		$response = $this->server->dispatch( $request );
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
315
316
		$response = $this->dispatch_request_signed_with_blog_token( $request );
317
318
		$this->assertEquals( 404, $response->get_status() ); // success in this context.
319
320
		$this->assertEquals( 'Comment not found', $response->get_data()['message'] );
321
	}
322
323
	/**
324
	 * Testing the `/jetpack/v4/posts/(?P<id>\d+)/backup` endpoint with admin user.
325
	 */
326
	public function test_backup_post_unauthorized() {
327
		wp_set_current_user( $this->admin_id );
328
329
		$request  = new WP_REST_Request( 'GET', '/jetpack/v4/posts/1/backup' );
330
		$response = $this->server->dispatch( $request );
331
332
		$this->assertEquals( 403, $response->get_status() );
333
		$this->assertEquals( 'You are not allowed to perform this action.', $response->get_data()['message'] );
334
	}
335
336
	/**
337
	 * Testing the `/jetpack/v4/posts/(?P<id>\d+)/backup endpoint on success.
338
	 */
339
	public function test_backup_post_success() {
340
		$post_id = wp_insert_post(
341
			array(
342
				'post_content' => 'dummy',
343
			)
344
		);
345
346
		$request = new WP_REST_Request( 'GET', "/jetpack/v4/posts/{$post_id}/backup" );
347
348
		$response = $this->server->dispatch( $request );
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
349
350
		$response = $this->dispatch_request_signed_with_blog_token( $request );
351
352
		$this->assertEquals( 200, $response->get_status() );
353
354
		$this->assertEquals( $post_id, $response->get_data()['post']['ID'] );
355
	}
356
357
	/**
358
	 * Testing the `/jetpack/v4/users/(?P<id>\d+)/backup` endpoint with admin user.
359
	 */
360
	public function test_backup_user_unauthorized() {
361
		wp_set_current_user( $this->admin_id );
362
363
		$request  = new WP_REST_Request( 'GET', '/jetpack/v4/users/1/backup' );
364
		$response = $this->server->dispatch( $request );
365
366
		$this->assertEquals( 403, $response->get_status() );
367
368
		$this->assertEquals( 'You are not allowed to perform this action.', $response->get_data()['message'] );
369
	}
370
371
	/**
372
	 * Testing the `/jetpack/v4/users/(?P<id>\d+)/backup endpoint on success.
373
	 */
374
	public function test_backup_user_success() {
375
		wp_set_current_user( $this->admin_id );
376
377
		$request = new WP_REST_Request( 'GET', "/jetpack/v4/users/{$this->admin_id}/backup" );
378
379
		$response = $this->server->dispatch( $request );
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
380
381
		$response = $this->dispatch_request_signed_with_blog_token( $request );
382
383
		$this->assertEquals( 200, $response->get_status() );
384
385
		$this->assertEquals( $this->admin_id, $response->get_data()['user']['ID'] );
386
	}
387
388
	/**
389
	 * Signs a request with a blog token before dispatching it.
390
	 *
391
	 * Ensures that these tests pass through Connection_Rest_Authentication::wp_rest_authenticate,
392
	 * because WP_REST_Server::dispatch doesn't call any auth logic (in a real
393
	 * request, this would all happen earlier).
394
	 *
395
	 * @param WP_REST_Request $request The request to sign before dispatching.
396
	 * @return WP_REST_Response
397
	 */
398
	private function dispatch_request_signed_with_blog_token( $request ) {
399
		add_filter( 'jetpack_options', array( $this, 'mock_jetpack_site_connection_options' ), 10, 2 );
400
401
		$token     = 'new:1:0';
402
		$timestamp = (string) time();
403
		$nonce     = 'testing123';
404
		$body_hash = '';
405
406
		$_SERVER['REQUEST_METHOD'] = 'POST';
407
408
		$_GET['_for']      = 'jetpack';
409
		$_GET['token']     = $token;
410
		$_GET['timestamp'] = $timestamp;
411
		$_GET['nonce']     = $nonce;
412
		$_GET['body-hash'] = $body_hash;
413
		// This is intentionally using base64_encode().
414
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
415
		$_GET['signature'] = base64_encode(
416
			hash_hmac(
417
				'sha1',
418
				implode(
419
					"\n",
420
					array(
421
						$token,
422
						$timestamp,
423
						$nonce,
424
						$body_hash,
425
						'POST',
426
						'anything.example',
427
						'80',
428
						'',
429
					)
430
				) . "\n",
431
				'blogtoken',
432
				true
433
			)
434
		);
435
436
		$jp_connection_auth = Connection_Rest_Authentication::init();
437
		$jp_connection_auth->wp_rest_authenticate( false );
438
439
		$response = $this->server->dispatch( $request );
440
441
		$jp_connection_auth->reset_saved_auth_state();
442
443
		remove_filter( 'jetpack_options', array( $this, 'mock_jetpack_site_connection_options' ) );
444
445
		return $response;
446
	}
447
448
	/**
449
	 * Intercept the `Jetpack_Options` call and mock the values.
450
	 * Site-level connection set-up.
451
	 *
452
	 * @param mixed  $value The current option value.
453
	 * @param string $name Option name.
454
	 *
455
	 * @return mixed
456
	 */
457
	public function mock_jetpack_site_connection_options( $value, $name ) {
458
		switch ( $name ) {
459
			case 'blog_token':
460
				return 'new.blogtoken';
461
			case 'id':
462
				return '999';
463
		}
464
465
		return $value;
466
	}
467
468
}
469