Passed
Push — master ( 6268c8...c33117 )
by Paul
14:11 queued 01:11
created

Router::routePublicAjaxRequest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 6
dl 0
loc 9
ccs 0
cts 7
cp 0
rs 10
c 1
b 1
f 0
cc 2
nc 2
nop 0
crap 6
1
<?php
2
3
namespace GeminiLabs\SiteReviews;
4
5
use GeminiLabs\SiteReviews\Helpers\Arr;
6
use GeminiLabs\SiteReviews\Modules\Notice;
7
use GeminiLabs\SiteReviews\Request;
8
9
class Router
10
{
11
    /**
12
     * @var array
13
     */
14
    protected $unguardedAdminActions;
15
16
    /**
17
     * @var array
18
     */
19
    protected $unguardedPublicActions;
20
21
    public function __construct()
22
    {
23
        $this->unguardedAdminActions = glsr()->filterArray('router/admin/unguarded-actions', [
24
            'dismiss-notice',
25
        ]);
26
        $this->unguardedPublicActions = glsr()->filterArray('router/public/unguarded-actions', [
27
            'fetch-paged-reviews',
28
            'submit-review',
29
        ]);
30
    }
31
32
    /**
33
     * @return void
34
     */
35 7
    public function routeAdminAjaxRequest()
36
    {
37 7
        $request = $this->getRequest();
38 7
        $this->checkAjaxRequest($request);
39 7
        if (glsr()->isAdmin()) { // if viewing a WP admin page
40
            if (!in_array($request->_action, $this->unguardedAdminActions)) {
41
                $this->checkAjaxNonce($request);
42
            }
43
        } else {
44 7
            if (!in_array($request->_action, $this->unguardedPublicActions)) {
45
                $this->checkAjaxNonce($request);
46
            }
47
        }
48 7
        $this->routeRequest('ajax', $request);
49
        wp_die();
50
    }
51
52
    /**
53
     * @return void
54
     */
55 7
    public function routeAdminPostRequest()
56
    {
57 7
        $request = $this->getRequest();
58 7
        if ($this->isValidPostRequest($request)) {
59
            check_admin_referer($request->_action); // die() called if nonce is invalid
60
            $this->routeRequest('admin', $request);
61
        }
62 7
    }
63
64
    /**
65
     * @return void
66
     */
67
    public function routePublicAjaxRequest()
68
    {
69
        $request = $this->getRequest();
70
        $this->checkAjaxRequest($request);
71
        if (!in_array($request->_action, $this->unguardedPublicActions)) {
72
            $this->checkAjaxNonce($request);
73
        }
74
        $this->routeRequest('ajax', $request);
75
        wp_die();
76
    }
77
78
    /**
79
     * @return void
80
     */
81
    public function routePublicPostRequest()
82
    {
83
        if (glsr()->isAdmin()) {
84
            return;
85
        }
86
        $request = $this->getRequest();
87
        if ($this->isValidPostRequest($request) && $this->isValidPublicNonce($request)) {
88
            $this->routeRequest('public', $request);
89
        }
90
    }
91
92
    /**
93
     * @return void
94
     */
95
    protected function checkAjaxNonce(Request $request)
96
    {
97
        if (empty($request->_nonce)) {
98
            $this->sendAjaxError('AJAX request is missing a nonce', $request, 400, 'Unauthorized request');
99
        }
100
        if (!wp_verify_nonce($request->_nonce, $request->_action)) {
101
            $this->sendAjaxError('AJAX request failed the nonce check', $request, 403, 'Unauthorized request');
102
        }
103
    }
104
105
    /**
106
     * @return void
107
     */
108 7
    protected function checkAjaxRequest(Request $request)
109
    {
110 7
        if (empty($request->_action)) {
111
            $this->sendAjaxError('AJAX request must include an action', $request, 400, 'Invalid request');
112
        }
113 7
        if (empty($request->_ajax_request)) {
114
            $this->sendAjaxError('AJAX request is invalid', $request, 400, 'Invalid request');
115
        }
116 7
    }
117
118
    /**
119
     * All ajax requests in the plugin are triggered by a single action hook: glsr_action,
120
     * while each ajax route is determined by $_POST[request][_action].
121
     * @return Request
122
     */
123 7
    protected function getRequest()
124
    {
125 7
        $request = Helper::filterInputArray(glsr()->id);
126 7
        if (Helper::filterInput('action') == glsr()->prefix.'action') {
127 7
            $request['_ajax_request'] = true;
128
        }
129 7
        if ('submit-review' == Helper::filterInput('_action', $request)) {
130 7
            $request['_recaptcha-token'] = Helper::filterInput('g-recaptcha-response');
131
        }
132 7
        return new Request($request);
133
    }
134
135
    /**
136
     * @return bool
137
     */
138 7
    protected function isValidPostRequest(Request $request)
139
    {
140 7
        return !empty($request->_action) && empty($request->_ajax_request);
141
    }
142
143
    /**
144
     * @return bool
145
     */
146
    protected function isValidPublicNonce(Request $request)
147
    {
148
        // only require a nonce for public requests if user is logged in, this avoids 
149
        // potential caching issues since unauthenticated requests should never be destructive.
150
        if (is_user_logged_in() && !wp_verify_nonce($request->_nonce, $request->_action)) {
151
            glsr_log()->error('nonce check failed for public request')->debug($request);
152
            return false;
153
        }
154
        return true;
155
    }
156
157
    /**
158
     * @param string $type
159
     * @return void
160
     */
161 7
    protected function routeRequest($type, Request $request)
162
    {
163 7
        $actionHook = "route/{$type}/{$request->_action}";
164 7
        $request = glsr()->filterArray('route/request', $request->toArray(), $request->_action, $type);
165 7
        $request = new Request($request);
166 7
        glsr()->action($actionHook, $request);
167
        if (0 === did_action(glsr()->id.'/'.$actionHook)) {
168
            glsr_log('Unknown '.$type.' router request: '.$request->_action);
169
        }
170
    }
171
172
    /**
173
     * @param string $error
174
     * @param int $code
175
     * @param string $message
176
     * @return void
177
     */
178
    protected function sendAjaxError($error, Request $request, $code = 400, $message = '')
179
    {
180
        glsr_log()->error($error)->debug($request);
181
        $notices = '';
182
        if (glsr()->isAdmin()) {
183
            glsr(Notice::class)->addError(_x('There was an error (try reloading the page).', 'admin-text', 'site-reviews').' <code>'.$error.'</code>');
184
            $notices = glsr(Notice::class)->get();
185
        }
186
        if ('submit-review' === $request->_action) {
187
            $message = __('The form could not be submitted. Please notify the site administrator.', 'site-reviews');
188
        }
189
        wp_send_json_error([
190
            'code' => $code,
191
            'error' => $error,
192
            'message' => $message ?: $error,
193
            'notices' => $notices,
194
        ]);
195
    }
196
}
197