Passed
Push — hotfix/fix-counts ( b4ff8e...673622 )
by Paul
04:39
created

Router::checkAjaxNonce()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 8.125

Importance

Changes 5
Bugs 1 Features 0
Metric Value
cc 5
eloc 7
nc 5
nop 1
dl 0
loc 10
ccs 4
cts 8
cp 0.5
crap 8.125
rs 9.6111
c 5
b 1
f 0
1
<?php
2
3
namespace GeminiLabs\SiteReviews;
4
5
use GeminiLabs\SiteReviews\Application;
6
use GeminiLabs\SiteReviews\Controllers\AdminController;
7
use GeminiLabs\SiteReviews\Controllers\AjaxController;
8
use GeminiLabs\SiteReviews\Controllers\PublicController;
9
use GeminiLabs\SiteReviews\Helper;
10
use GeminiLabs\SiteReviews\Modules\Notice;
11
12
class Router
13
{
14
	/**
15
	 * @var array
16
	 */
17
	protected $unguardedActions = [];
18
19
	public function __construct()
20
	{
21
		$this->unguardedActions = apply_filters( 'site-reviews/router/unguarded-actions', [
22
			'fetch-paged-reviews',
23
		]);
24
	}
25
26
	/**
27
	 * @return void
28
	 */
29 1
	public function routeAdminPostRequest()
30
	{
31 1
		$request = $this->getRequest();
32 1
		if( !$this->isValidPostRequest( $request ))return;
33
		check_admin_referer( $request['_action'] );
34
		$this->routeRequest( 'admin', $request['_action'], $request );
35
	}
36
37
	/**
38
	 * @return void
39
	 */
40 1
	public function routeAjaxRequest()
41
	{
42 1
		$request = $this->getRequest();
43 1
		$this->checkAjaxRequest( $request );
44 1
		$this->checkAjaxNonce( $request );
45 1
		$this->routeRequest( 'ajax', $request['_action'], $request );
46
		wp_die();
47
	}
48
49
	/**
50
	 * @return void
51
	 */
52
	public function routePublicPostRequest()
53
	{
54
		if( is_admin() )return;
55
		$request = $this->getRequest();
56
		if( !$this->isValidPostRequest( $request ))return;
57
		if( !$this->isValidPublicNonce( $request ))return;
58
		$this->routeRequest( 'public', $request['_action'], $request );
59
	}
60
61
	/**
62
	 * @return void
63
	 */
64 1
	protected function checkAjaxNonce( array $request )
65
	{
66 1
		if( !is_user_logged_in()
67 1
			|| in_array( glsr_get( $request, '_action' ), $this->unguardedActions )
68 1
		)return;
69
		if( !isset( $request['_nonce'] )) {
70
			$this->sendAjaxError( 'request is missing a nonce', $request );
71
		}
72
		if( !wp_verify_nonce( $request['_nonce'], $request['_action'] )) {
73
			$this->sendAjaxError( 'request failed the nonce check', $request, 403 );
74
		}
75
	}
76
77
	/**
78
	 * @return void
79
	 */
80 1
	protected function checkAjaxRequest( array $request )
81
	{
82 1
		if( !isset( $request['_action'] )) {
83
			$this->sendAjaxError( 'request must include an action', $request );
84
		}
85 1
		if( empty( $request['_ajax_request'] )) {
86
			$this->sendAjaxError( 'request is invalid', $request );
87
		}
88 1
	}
89
90
	/**
91
	 * All ajax requests in the plugin are triggered by a single action hook: glsr_action,
92
	 * while each ajax route is determined by $_POST[request][_action]
93
	 * @return array
94
	 */
95 1
	protected function getRequest()
96
	{
97 1
		$request = glsr( Helper::class )->filterInputArray( Application::ID );
98 1
		if( glsr( Helper::class )->filterInput( 'action' ) == Application::PREFIX.'action' ) {
99 1
			$request['_ajax_request'] = true;
100
		}
101 1
		if( glsr( Helper::class )->filterInput( '_action', $request ) == 'submit-review' ) {
102 1
			$request['_recaptcha-token'] = glsr( Helper::class )->filterInput( 'g-recaptcha-response' );
103
		}
104 1
		return $request;
105
	}
106
107
	/**
108
	 * @return bool
109
	 */
110 1
	protected function isValidPostRequest( array $request = [] )
111
	{
112 1
		return !empty( $request['_action'] ) && empty( $request['_ajax_request'] );
113
	}
114
115
	/**
116
	 * @return bool
117
	 */
118
	protected function isValidPublicNonce( array $request )
119
	{
120
		if( is_user_logged_in() && !wp_verify_nonce( $request['_nonce'], $request['_action'] )) {
121
			glsr_log()->error( 'nonce check failed for public request' )->debug( $request );
122
			return false;
123
		}
124
		return true;
125
	}
126
127
	/**
128
	 * @param string $type
129
	 * @param string $action
130
	 * @return void
131
	 */
132 1
	protected function routeRequest( $type, $action, array $request = [] )
133
	{
134 1
		$actionHook = 'site-reviews/route/'.$type.'/request';
135 1
		$controller = glsr( glsr( Helper::class )->buildClassName( $type.'-controller', 'Controllers' ));
136 1
		$method = glsr( Helper::class )->buildMethodName( $action, 'router' );
137 1
		$request = apply_filters( 'site-reviews/route/request', $request, $action, $type );
138 1
		do_action( $actionHook, $action, $request );
139 1
		if( is_callable( [$controller, $method] )) {
140 1
			call_user_func( [$controller, $method], $request );
141
			return;
142
		}
143
		if( did_action( $actionHook ) === 0 ) {
144
			glsr_log( 'Unknown '.$type.' router request: '.$action );
145
		}
146
	}
147
148
	/**
149
	 * @param string $error
150
	 * @param int $statusCode
151
	 * @return void
152
	 */
153
	protected function sendAjaxError( $error, array $request, $statusCode = 400 )
154
	{
155
		glsr_log()->error( $error )->debug( $request );
156
		glsr( Notice::class )->addError( __( 'There was an error (try refreshing the page).', 'site-reviews' ).' <code>'.$error.'</code>' );
157
		wp_send_json_error([
158
			'message' => __( 'The form could not be submitted. Please notify the site administrator.', 'site-reviews' ),
159
			'notices' => glsr( Notice::class )->get(),
160
			'error' => $error,
161
		]);
162
	}
163
}
164