Passed
Push — master ( 6fa460...6bbb33 )
by Paul
18:19 queued 07:35
created

Router::unguardedPublicActions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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