PluginProbe
Plugin Detective – Troubleshooting Conflicts / 1.2.36
Plugin Detective – Troubleshooting Conflicts v1.2.36
1.2.36 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 All 55 releases
← All changes | troubleshoot/includes/class-api.php +265 -264 1.1.4 → 1.2.36 View file →
@@ -1,264 +1,265 @@
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 ) ) {
120 - $this->request = $_REQUEST;
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 - $auth_response = $this->plugin->auth->get_nonce( $this->params['username'], $this->params['password'], 'pd_api' );
168 -
169 - if ( is_a( $auth_response, 'WP_Error' ) ) {
170 - $this->errors['authentication'] = array_keys( $auth_response->errors );
171 - $this->errors['authentication'] = $this->errors['authentication']['0'];
172 - } else {
173 - $this->data['nonce'] = $auth_response;
174 - }
175 -
176 - $user_response = $this->plugin->auth->get_user( $this->params['username'], $this->params['password'], 'pd_api' );
177 -
178 - if ( is_a( $user_response, 'WP_Error' ) ) {
179 - $this->errors['authentication'] = array_keys( $user_response->errors );
180 - $this->errors['authentication'] = $this->errors['authentication']['0'];
181 - } else {
182 - $this->data['user'] = $user_response;
183 - }
184 -
185 - $this->return_response();
186 - }
187 -
188 -
189 - // Verify nonce value
190 - if ( empty( $this->params['nonce'] ) ) {
191 - $this->errors['nonce'] = 'Required for authenticated requests';
192 - $this->return_response();
193 - }
194 -
195 - if ( ! $this->plugin->auth->verify_nonce( $this->params['nonce'], 'pd_api' ) ) {
196 - $this->errors['nonce'] = 'Invalid';
197 - $this->return_response();
198 - }
199 -
200 -
201 - // Process request
202 - $object = $this;
203 - if ( !empty( $this->params['controller'] ) ) {
204 - if ( !in_array( $this->params['controller'], array(
205 - 'plugins',
206 - 'installed',
207 - 'cases',
208 - 'clues',
209 - ) ) ) {
210 - $this->errors['controller'] = 'Trying to access a disallowed controller';
211 - $this->return_response();
212 - }
213 -
214 - if ( !property_exists( $this->plugin, $this->params['controller'] ) ) {
215 - $this->errors['controller'] = 'Controller not found';
216 - $this->return_response();
217 - }
218 - $controller = $this->params['controller'];
219 - $object = $this->plugin->$controller;
220 - }
221 -
222 - if ( !method_exists( $object, $this->params['action'] ) ) {
223 - $this->errors['action'] = 'Action not found';
224 - $this->return_response();
225 - }
226 -
227 - $action = $this->params['action'];
228 -
229 - $response = $object->$action( $this->args );
230 - if ( is_a( $response, 'WP_Error' ) ) {
231 - $this->errors[$response->get_error_code()] = $response->get_error_message();
232 - } else {
233 - $this->data = $response;
234 - }
235 -
236 - $this->return_response();
237 - }
238 -
239 - public function test() {
240 - return array( 'test' => 'value',);
241 - }
242 -
243 - public function return_response() {
244 - header( 'Content-type: application/json' );
245 - echo json_encode( array(
246 - 'data' => $this->data,
247 - 'errors' => $this->errors,
248 - ) );
249 -
250 - exit();
251 - }
252 -
253 - public static function is_string( $string ) {
254 - if ( !is_string( $string ) ) {
255 - return 'String value required';
256 - }
257 -
258 - if ( (string)(int)$string === (string)$string ) {
259 - return 'Non-numeric value';
260 - }
261 -
262 - return true;
263 - }
264 -}
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 +}