PluginProbe
Plugin Detective – Troubleshooting Conflicts / 1.2.35
Plugin Detective – Troubleshooting Conflicts v1.2.35
1.2.35 1.2.33 1.2.32 1.2.31 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2 1.2.1 1.2.10 1.2.12 1.2.13 1.2.14 1.2.16 1.2.19 1.2.20 1.2.22 1.2.23 1.2.24 All 54 releases
plugin-detective / troubleshoot / includes / class-api.php

class-api.php in Plugin Detective – Troubleshooting Conflicts 1.2.35, at troubleshoot/includes/class-api.php

266 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Troubleshoot Api.
4 *
5 * @since 0.0.0
6 * @package Troubleshoot
7 */
8
9 /**
10 * Troubleshoot Api.
11 *
12 * @since 0.0.0
13 */
14 class PDT_Api {
15 /**
16 * Parent plugin class.
17 *
18 * @since 0.0.0
19 *
20 * @var Troubleshoot
21 */
22 protected $plugin = null;
23
24 public $params = array();
25 public $args = array();
26 public $errors = array();
27 public $data = array();
28
29
30 /**
31 * Constructor.
32 *
33 * @since 0.0.0
34 *
35 * @param Troubleshoot $plugin Main plugin object.
36 */
37 public function __construct( $plugin ) {
38 $this->plugin = $plugin;
39 $this->hooks();
40 }
41
42 /**
43 * Initiate our hooks.
44 *
45 * @since 0.0.0
46 */
47 public function hooks() {
48
49 }
50
51 public function process_params() {
52 $param_keys = array(
53 'nonce' => array(
54 'is_required' => false,
55 'escape_callback' => 'esc_attr',
56 'validation_callback' => '__return_true',
57 ),
58
59 'controller' => array(
60 'is_required' => false,
61 'escape_callback' => 'esc_attr',
62 'validation_callback' => '__return_true',
63 ),
64
65 'action' => array(
66 'is_required' => true,
67 'escape_callback' => 'esc_attr',
68 'validation_callback' => array( 'PDT_Api', 'is_string' ),
69 ),
70
71 'username' => array(
72 'is_required' => false,
73 'escape_callback' => 'esc_attr',
74 'validation_callback' => '__return_true',
75 ),
76
77 'password' => array(
78 'is_required' => false,
79 'escape_callback' => 'esc_attr',
80 'validation_callback' => '__return_true',
81 ),
82 );
83
84 foreach ($param_keys as $param_key => $param_options) {
85 if ( !isset( $this->request[$param_key] ) ) {
86 if ( $param_options['is_required'] ) {
87 $this->errors[$param_key] = 'Required parameter';
88 }
89
90 $this->params[$param_key] = null;
91 continue;
92 }
93
94 $param_value = call_user_func( $param_options['escape_callback'], $this->request[$param_key] );
95
96 $validation_response = call_user_func( $param_options['validation_callback'], $param_value );
97 if ( true !== $validation_response ) {
98 $this->errors[$param_key] = $validation_response;
99 continue;
100 }
101
102 $this->params[$param_key] = $param_value;
103 }
104
105 if ( empty( $this->params['nonce'] ) && $this->params['action'] == 'authenticate' ) {
106 if ( empty( $this->params['username'] ) ) {
107 $this->errors['username'] = 'Required for authentication';
108 }
109
110 if ( empty( $this->params['password'] ) ) {
111 $this->errors['password'] = 'Required for authentication';
112 }
113 }
114 }
115
116 public function process_input() {
117 $this->request = array();
118
119 if ( ! empty( $_REQUEST ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Custom troubleshooter API authenticated via PDT_Auth nonce; runs standalone when the WP REST API is unavailable.
120 $this->request = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- See above; params validated downstream in process_params().
121 }
122
123 $json = file_get_contents("php://input");
124 if ( empty( $json ) ) {
125 return;
126 }
127
128 $array = json_decode( $json, true );
129 if ( empty( $array ) ) {
130 if ( empty( $this->request ) ) {
131 $this->errors['no_input'] = __( 'No input detected, possibly due to a malformed request or server setting. Please contact support', 'plugin-detective' );
132 }
133
134 return;
135 }
136
137 $this->request = array_merge( $this->request, $array );
138 }
139
140 public function process_args() {
141 $args = $this->request;
142 foreach ($this->params as $key => $value) {
143 unset( $args[$key] );
144 }
145 $this->args = $args;
146 }
147
148 public function process_request() {
149 if ( empty( $this->request ) ) {
150 $this->process_input();
151 }
152
153 if ( empty( $this->params ) ) {
154 $this->process_params();
155 }
156
157 if ( empty( $this->args ) ) {
158 $this->process_args();
159 }
160
161 if ( !empty( $this->errors ) ) {
162 $this->return_response();
163 }
164
165 // Authentication Request
166 if ( $this->params['action'] == 'authenticate' ) {
167 $user = $this->plugin->auth->authenticate( $this->params['username'], $this->params['password'] );
168
169 if ( is_a( $user, 'WP_Error' ) ) {
170 // Single, fixed error code for every failure mode — never echo a
171 // code that distinguishes a valid username from an invalid one.
172 $this->errors['authentication'] = $user->get_error_code();
173 } else {
174 $this->data['nonce'] = $this->plugin->auth->create_nonce( 'pd_api', $user->ID );
175 }
176
177 $this->return_response();
178 }
179
180
181 // Verify nonce value
182 if ( empty( $this->params['nonce'] ) ) {
183 $this->errors['nonce'] = 'Required for authenticated requests';
184 $this->return_response();
185 }
186
187 $nonce_user_id = $this->plugin->auth->verify_nonce( $this->params['nonce'], 'pd_api' );
188 if ( empty( $nonce_user_id ) ) {
189 $this->errors['nonce'] = 'Invalid';
190 $this->return_response();
191 }
192
193 // The nonce is bound to the user it was issued for. Re-check that user still
194 // has plugin-management rights so a token can never authorize more than its
195 // owner, even if it leaks or the user's role is later downgraded.
196 if ( ! user_can( $nonce_user_id, 'activate_plugins' ) ) {
197 $this->errors['permission'] = 'You do not have permission to perform this action';
198 $this->return_response();
199 }
200
201
202 // Process request
203 $object = $this;
204 if ( !empty( $this->params['controller'] ) ) {
205 if ( !in_array( $this->params['controller'], array(
206 'plugins',
207 'installed',
208 'cases',
209 'clues',
210 ) ) ) {
211 $this->errors['controller'] = 'Trying to access a disallowed controller';
212 $this->return_response();
213 }
214
215 if ( !property_exists( $this->plugin, $this->params['controller'] ) ) {
216 $this->errors['controller'] = 'Controller not found';
217 $this->return_response();
218 }
219 $controller = $this->params['controller'];
220 $object = $this->plugin->$controller;
221 }
222
223 if ( !method_exists( $object, $this->params['action'] ) ) {
224 $this->errors['action'] = 'Action not found';
225 $this->return_response();
226 }
227
228 $action = $this->params['action'];
229
230 $response = $object->$action( $this->args );
231 if ( is_a( $response, 'WP_Error' ) ) {
232 $this->errors[$response->get_error_code()] = $response->get_error_message();
233 } else {
234 $this->data = $response;
235 }
236
237 $this->return_response();
238 }
239
240 public function test() {
241 return array( 'test' => 'value',);
242 }
243
244 public function return_response() {
245 header( 'Content-type: application/json' );
246 echo json_encode( array(
247 'data' => $this->data,
248 'errors' => $this->errors,
249 ) );
250
251 exit();
252 }
253
254 public static function is_string( $string ) {
255 if ( !is_string( $string ) ) {
256 return 'String value required';
257 }
258
259 if ( (string)(int)$string === (string)$string ) {
260 return 'Non-numeric value';
261 }
262
263 return true;
264 }
265 }
266