PluginProbe
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More / 2.0.2
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More v2.0.2
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 47 releases
content-control / vendor-prefixed / trustedlogin / client / src / Remote.php

Remote.php in Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More 2.0.2, at vendor-prefixed/trustedlogin/client/src/Remote.php

351 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Remote
4 *
5 * @package ContentControl\Vendor\TrustedLogin\Client
6 *
7 * @copyright 2021 Katz Web Services, Inc.
8 *
9 * @license GPL-2.0-or-later
10 * Modified by code-atlantic on 18-September-2023 using Strauss.
11 * @see https://github.com/BrianHenryIE/strauss
12 */
13
14 namespace ContentControl\Vendor\TrustedLogin;
15
16 // Exit if accessed directly
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 use \Exception;
22 use \WP_Error;
23 use \WP_User;
24 use \WP_Admin_Bar;
25
26 /**
27 * The TrustedLogin all-in-one drop-in class.
28 */
29 final class Remote {
30
31 /**
32 * @var string The API url for the TrustedLogin SaaS Platform (with trailing slash)
33 * @since 1.0.0
34 */
35 const API_URL = 'https://app.trustedlogin.com/api/v1/';
36
37 /**
38 * @var Config $config
39 */
40 private $config;
41
42 /**
43 * @var Logging $logging
44 */
45 private $logging;
46
47 /**
48 * SupportUser constructor.
49 */
50 public function __construct( Config $config, Logging $logging ) {
51 $this->config = $config;
52 $this->logging = $logging;
53 }
54
55 public function init() {
56 add_action( 'trustedlogin/' . $this->config->ns() . '/access/created', array( $this, 'maybe_send_webhook' ) );
57 add_action( 'trustedlogin/' . $this->config->ns() . '/access/extended', array( $this, 'maybe_send_webhook' ) );
58 add_action( 'trustedlogin/' . $this->config->ns() . '/access/revoked', array( $this, 'maybe_send_webhook' ) );
59 add_action( 'trustedlogin/' . $this->config->ns() . '/logged_in', array( $this, 'maybe_send_webhook' ) );
60 }
61
62 /**
63 * POSTs to `webhook/url`, if defined in the configuration array.
64 *
65 * @since 1.0.0
66 * @since 1.4.0 $data now includes the `$access_key` and `$debug_data` keys.
67 * @since 1.5.0 $data now includes the `$ticket` key.
68 *
69 * @param array $data {
70 * @type string $url The site URL as returned by get_site_url().
71 * @type string $ns Namespace of the plugin.
72 * @type string $action "created", "extended", "logged_in", or "revoked".
73 * @type string $access_key The access key.
74 * @type string $debug_data (Optional) Site debug data from {@see WP_Debug_Data::debug_data()}, sent if `webhook/debug_data` is true.
75 * @type string $ref (Optional) Support ticket Reference ID.
76 * @type array $ticket (Optional) Support ticket provided by customer with `message` key.
77 * }
78 *
79 * @return bool|WP_Error False: webhook setting not defined; True: success; WP_Error: error!
80 */
81 public function maybe_send_webhook( $data ) {
82
83 $webhook_url = $this->config->get_setting( 'webhook/url' );
84
85 if ( ! $webhook_url ) {
86 // Back compatibility with v1–v1.3.4.
87 $webhook_url = $this->config->get_setting( 'webhook_url' );
88 }
89
90 if ( ! $webhook_url ) {
91 return false;
92 }
93
94 if ( ! wp_http_validate_url( $webhook_url ) ) {
95
96 $error = new \WP_Error( 'invalid_webhook_url', 'An invalid `webhook/url` setting was passed to the TrustedLogin Client: ' . esc_attr( $webhook_url ) );
97
98 $this->logging->log( $error, __METHOD__, 'error' );
99
100 return $error;
101 }
102
103 try {
104
105 $posted = wp_remote_post( $webhook_url, array( 'body' => $data ) );
106
107 if ( is_wp_error( $posted ) ) {
108 $this->logging->log( 'An error encountered while sending a webhook to ' . esc_attr( $webhook_url ), __METHOD__, 'error', $posted );
109
110 return $posted;
111 }
112
113 $this->logging->log( 'Webhook was sent to ' . esc_attr( $webhook_url ), __METHOD__, 'debug', $data );
114
115 return true;
116
117 } catch ( Exception $exception ) {
118
119 $this->logging->log( 'A fatal error was triggered while sending a webhook to ' . esc_attr( $webhook_url ) . ': ' . $exception->getMessage(), __METHOD__, 'error' );
120
121 return new \WP_Error( $exception->getCode(), $exception->getMessage() );
122 }
123 }
124
125 /**
126 * API Function: send the API request
127 *
128 * @since 1.0.0
129 *
130 * @param string $path - the path for the REST API request (no initial or trailing slash needed)
131 * @param array $data Data passed as JSON-encoded body for
132 * @param string $method
133 * @param array $additional_headers - any additional headers required for auth/etc
134 *
135 * @return array|WP_Error wp_remote_request() response or WP_Error if something went wrong
136 */
137 public function send( $path, $data, $method = 'POST', $additional_headers = array() ) {
138
139 $method = is_string( $method ) ? strtoupper( $method ) : $method;
140
141 if ( ! is_string( $method ) || ! in_array( $method, array(
142 'POST',
143 'PUT',
144 'GET',
145 'HEAD',
146 'PUSH',
147 'DELETE',
148 ), true ) ) {
149 $this->logging->log( sprintf( 'Error: Method not in allowed array list (%s)', print_r( $method, true ) ), __METHOD__, 'critical' );
150
151 return new \WP_Error( 'invalid_method', sprintf( 'Error: HTTP method "%s" is not in the list of allowed methods', print_r( $method, true ) ) );
152 }
153
154 $headers = array(
155 'Accept' => 'application/json',
156 'Content-Type' => 'application/json',
157 'Authorization' => 'Bearer ' . $this->config->get_setting( 'auth/api_key' ),
158 );
159
160 if ( ! empty( $additional_headers ) ) {
161 $headers = array_merge( $headers, $additional_headers );
162 }
163
164 $request_options = array(
165 'method' => $method,
166 'timeout' => 15,
167 'httpversion' => '1.1',
168 'headers' => $headers,
169 );
170
171 if ( ! empty( $data ) && ! in_array( $method, array( 'GET', 'HEAD' ), true ) ) {
172 $request_options['body'] = wp_json_encode( $data );
173 }
174
175 try {
176 $api_url = $this->build_api_url( $path );
177
178 $this->logging->log( sprintf( 'Sending to %s: %s', $api_url, print_r( $request_options, true ) ), __METHOD__, 'debug' );
179
180 $response = wp_remote_request( $api_url, $request_options );
181
182 } catch ( Exception $exception ) {
183
184 $error = new \WP_Error( 'wp_remote_request_exception', sprintf( 'There was an exception during the remote request: %s (%s)', $exception->getMessage(), $exception->getCode() ) );
185
186 $this->logging->log( $error, __METHOD__, 'error' );
187
188 return $error;
189 }
190
191 $this->logging->log( sprintf( 'Response: %s', print_r( $response, true ) ), __METHOD__, 'debug' );
192
193 return $response;
194 }
195
196 /**
197 * Builds URL to API endpoints
198 *
199 * @since 1.0.0
200 *
201 * @param string $endpoint Endpoint to hit on the API; example "sites" or "sites/{$site_identifier}"
202 *
203 * @return string
204 */
205 private function build_api_url( $endpoint = '' ) {
206
207 /**
208 * Modifies the endpoint URL for the TrustedLogin service.
209 *
210 * @internal This allows pointing requests to testing servers.
211 *
212 * @param string $url URL to TrustedLogin API.
213 *
214 */
215 $base_url = apply_filters( 'trustedlogin/' . $this->config->ns() . '/api_url', self::API_URL );
216
217 if ( is_string( $endpoint ) ) {
218 $url = trailingslashit( $base_url ) . $endpoint;
219 } else {
220 $url = trailingslashit( $base_url );
221 }
222
223 return $url;
224 }
225
226 /**
227 * Translates response codes to more nuanced error descriptions specific to TrustedLogin.
228 *
229 * @param array|WP_Error $api_response Response from HTTP API
230 *
231 * @return int|WP_Error|null If valid response, the response code ID or null. If error, a WP_Error with a message description.
232 */
233 static public function check_response_code( $api_response ) {
234
235 if ( is_wp_error( $api_response ) ) {
236 $response_code = $api_response->get_error_code();
237 } else {
238 $response_code = wp_remote_retrieve_response_code( $api_response );
239 }
240
241 switch ( $response_code ) {
242
243 // Successful response, but no sites found.
244 case 204:
245 return null;
246
247 case 400:
248 case 423:
249 return new \WP_Error( 'unable_to_verify', esc_html__( 'Unable to verify Pause Mode.', 'trustedlogin' ), $api_response );
250
251 case 401:
252 return new \WP_Error( 'unauthenticated', esc_html__( 'Authentication failed.', 'trustedlogin' ), $api_response );
253
254 case 402:
255 return new \WP_Error( 'account_error', esc_html__( 'TrustedLogin account issue.', 'trustedlogin' ), $api_response );
256
257 case 403:
258 return new \WP_Error( 'invalid_token', esc_html__( 'Invalid tokens.', 'trustedlogin' ), $api_response );
259
260 // the KV store was not found, possible issue with endpoint
261 case 404:
262 return new \WP_Error( 'not_found', esc_html__( 'The TrustedLogin vendor was not found.', 'trustedlogin' ), $api_response );
263
264 // The site is a teapot.
265 case 418:
266 return new \WP_Error( 'teapot', '🫖', $api_response );
267
268 // Server offline
269 case 500:
270 case 503:
271 case 'http_request_failed':
272 return new \WP_Error( 'unavailable', esc_html__( 'The TrustedLogin site is not currently online.', 'trustedlogin' ), $api_response );
273
274 // Server error
275 case 501:
276 case 502:
277 case 522:
278 return new \WP_Error( 'server_error', esc_html__( 'The TrustedLogin site is not currently available.', 'trustedlogin' ), $api_response );
279
280 // wp_remote_retrieve_response_code() couldn't parse the $api_response
281 case '':
282 return new \WP_Error( 'invalid_response', esc_html__( 'Invalid response.', 'trustedlogin' ), $api_response );
283
284 default:
285 return (int) $response_code;
286 }
287 }
288
289 /**
290 * API Response Handler
291 *
292 * @since 1.0.0
293 *
294 * @param array|WP_Error $api_response - the response from HTTP API
295 * @param array $required_keys If the response JSON must have specific keys in it, pass them here
296 *
297 * @return array|WP_Error|null If successful response, returns array of JSON data. If failed, returns WP_Error. If
298 */
299 public function handle_response( $api_response, $required_keys = array() ) {
300
301 $response_code = self::check_response_code( $api_response );
302
303 // Null means a successful response, but does not return any body content (204). We can return early.
304 if ( null === $response_code ) {
305 return null;
306 }
307
308 if ( is_wp_error( $response_code ) ) {
309 $this->logging->log( "Response code check failed: " . print_r( $response_code, true ), __METHOD__, 'error' );
310
311 return $response_code;
312 }
313
314 $response_body = wp_remote_retrieve_body( $api_response );
315
316 if ( empty( $response_body ) ) {
317 $this->logging->log( "Response body not set: " . print_r( $response_body, true ), __METHOD__, 'error' );
318
319 return new \WP_Error( 'missing_response_body', esc_html__( 'The response was invalid.', 'trustedlogin' ), $api_response );
320 }
321
322 $response_json = json_decode( $response_body, true );
323
324 if ( empty( $response_json ) ) {
325 return new \WP_Error( 'invalid_response', esc_html__( 'Invalid response.', 'trustedlogin' ), $response_body );
326 }
327
328 if ( isset( $response_json['errors'] ) ) {
329
330 $errors = '';
331
332 // Multi-dimensional; we flatten.
333 foreach ( $response_json['errors'] as $key => $error ) {
334 $error = is_array( $error ) ? reset( $error ) : $error;
335 $errors .= $error;
336 }
337
338 return new \WP_Error( 'errors_in_response', esc_html( $errors ), $response_body );
339 }
340
341 foreach ( (array) $required_keys as $required_key ) {
342 if ( ! isset( $response_json[ $required_key ] ) ) {
343 // translators: %s is the name of the missing data from the server
344 return new \WP_Error( 'missing_required_key', sprintf( esc_html__( 'Invalid response. Missing key: %s', 'trustedlogin' ), $required_key ), $response_body );
345 }
346 }
347
348 return $response_json;
349 }
350 }
351