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 / Config.php

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

457 lines 13.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Config
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 ArrayAccess;
22 use \Exception;
23 use \WP_Error;
24
25 final class Config {
26
27 /**
28 * @var string[] These namespaces cannot be used, lest they result in confusion.
29 */
30 private static $reserved_namespaces = array( 'trustedlogin', 'client', 'vendor', 'admin', 'wordpress' );
31
32 /**
33 * @var array Default settings values
34 * @since 1.0.0
35 * @link https://www.trustedlogin.com/configuration/ Read the configuration settings documentation
36 */
37 private $default_settings = array(
38 'auth' => array(
39 'api_key' => null,
40 'license_key' => null,
41 ),
42 'caps' => array(
43 'add' => array(),
44 'remove' => array(),
45 ),
46 'decay' => WEEK_IN_SECONDS,
47 'logging' => array(
48 'enabled' => false,
49 'directory' => null,
50 'threshold' => 'notice',
51 'options' => array(
52 'extension' => 'log',
53 'dateFormat' => 'Y-m-d G:i:s.u',
54 'filename' => null, // Overridden in Logging.php
55 'flushFrequency' => false,
56 'logFormat' => false,
57 'appendContext' => true,
58 ),
59 ),
60 'menu' => array(
61 'slug' => null,
62 'title' => null,
63 'priority' => null,
64 'icon_url' => '',
65 'position' => null,
66 ),
67 'paths' => array(
68 'css' => null,
69 'js' => null, // Default is defined in get_default_settings()
70 ),
71 'reassign_posts' => true,
72 'require_ssl' => true,
73 'role' => 'editor',
74 'clone_role' => true,
75 'terms_of_service' => array(
76 'url' => null,
77 ),
78 'vendor' => array(
79 'namespace' => null,
80 'title' => null,
81 'email' => null,
82 'website' => null,
83 'support_url' => null,
84 'display_name' => null,
85 'logo_url' => null,
86 'about_live_access_url' => null,
87 ),
88 'webhook' => array(
89 'url' => null,
90 'debug_data' => false,
91 'create_ticket' => false,
92 ),
93 );
94
95 /**
96 * @var array $settings Configuration array after parsed and validated
97 * @since 1.0.0
98 */
99 private $settings = array();
100
101 /**
102 * Config constructor.
103 *
104 * @param array $settings
105 *
106 * @throws \Exception
107 */
108 public function __construct( array $settings = array() ) {
109
110 if ( empty( $settings ) ) {
111 throw new Exception( 'Developer: TrustedLogin requires a configuration array. See https://trustedlogin.com/configuration/ for more information.', 400 );
112 }
113
114 $this->settings = $settings;
115 }
116
117
118 /**
119 * @return true|\WP_Error[]
120 * @throws \Exception
121 *
122 */
123 public function validate() {
124
125 if ( in_array( __NAMESPACE__, array(
126 'ReplaceMe',
127 'ReplaceMe\GravityView\TrustedLogin',
128 ) ) && ! defined( 'TL_DOING_TESTS' ) ) {
129 throw new Exception( 'Developer: make sure to change the namespace for the TrustedLogin class. See https://trustedlogin.com/configuration/ for more information.', 501 );
130 }
131
132 $errors = array();
133
134 if ( ! isset( $this->settings['auth']['api_key'] ) ) {
135 $errors[] = new \WP_Error( 'missing_configuration', 'You need to set an API key. Get yours at https://app.trustedlogin.com' );
136 }
137
138 if ( isset( $this->settings['vendor']['website'] ) ) {
139 if ( 'https://www.example.com' === $this->settings['vendor']['website'] && ! defined( 'TL_DOING_TESTS' ) ) {
140 $errors[] = new \WP_Error( 'missing_configuration', 'You need to configure the "website" URL to point to the URL where the Vendor plugin is installed.' );
141 }
142 }
143
144 foreach ( array( 'namespace', 'title', 'website', 'support_url', 'email' ) as $required_vendor_field ) {
145 if ( ! isset( $this->settings['vendor'][ $required_vendor_field ] ) ) {
146 $errors[] = new \WP_Error( 'missing_configuration', sprintf( 'Missing required configuration: `vendor/%s`', $required_vendor_field ) );
147 }
148 }
149
150 if ( isset( $this->settings['decay'] ) ) {
151 if ( ! is_int( $this->settings['decay'] ) ) {
152 $errors[] = new \WP_Error( 'invalid_configuration', 'Decay must be an integer (number of seconds).' );
153 } elseif ( $this->settings['decay'] > MONTH_IN_SECONDS ) {
154 $errors[] = new \WP_Error( 'invalid_configuration', 'Decay must be less than or equal to 30 days.' );
155 } elseif ( $this->settings['decay'] < DAY_IN_SECONDS ) {
156 $errors[] = new \WP_Error( 'invalid_configuration', 'Decay must be greater than 1 day.' );
157 }
158 }
159
160 if ( isset( $this->settings['vendor']['namespace'] ) ) {
161
162 /**
163 * This seems like a reasonable max limit on the ns length.
164 *
165 * @see https://developer.wordpress.org/reference/functions/set_transient/#more-information
166 */
167 if ( strlen( $this->settings['vendor']['namespace'] ) > 96 ) {
168 $errors[] = new \WP_Error( 'invalid_configuration', 'Namespace length must be shorter than 96 characters.' );
169 }
170
171 if ( in_array( strtolower( $this->settings['vendor']['namespace'] ), self::$reserved_namespaces, true ) ) {
172 $errors[] = new \WP_Error( 'invalid_configuration', 'The defined namespace is reserved.' );
173 }
174 }
175
176 if ( isset( $this->settings['vendor']['email'] ) && ! filter_var( $this->settings['vendor']['email'], FILTER_VALIDATE_EMAIL ) ) {
177 $errors[] = new \WP_Error( 'invalid_configuration', 'An invalid `vendor/email` setting was passed to the TrustedLogin Client.' );
178 }
179
180 // TODO: Add ns collision check?
181
182 foreach ( array( 'webhook/url', 'webhook_url', 'vendor/support_url', 'vendor/website' ) as $settings_key ) {
183 $value = $this->get_setting( $settings_key, '', $this->settings );
184 $url = wp_kses_bad_protocol( $value, array( 'http', 'https' ) );
185 if ( $value && ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
186 $errors[] = new \WP_Error(
187 'invalid_configuration',
188 sprintf( 'An invalid `%s` setting was passed to the TrustedLogin Client: %s',
189 $settings_key,
190 print_r( $this->get_setting( $settings_key, null, $this->settings ), true )
191 )
192 );
193 }
194 }
195
196 if ( false !== $this->get_setting( 'clone_role', true, $this->settings ) ) {
197 $added_caps = $this->get_setting( 'caps/add', array(), $this->settings );
198
199 foreach ( SupportRole::$prevented_caps as $invalid_cap ) {
200 if ( array_key_exists( $invalid_cap, $added_caps ) ) {
201 $errors[] = new \WP_Error( 'invalid_configuration', 'TrustedLogin users cannot be allowed to: ' . $invalid_cap );
202 }
203 }
204 } else {
205 $added_caps = $this->get_setting( 'caps/add', array(), $this->settings );
206 $removed_caps = $this->get_setting( 'caps/remove', array(), $this->settings );
207
208 $added_caps = array_filter( $added_caps );
209 $removed_caps = array_filter( $removed_caps );
210
211 if ( ! empty( $added_caps ) || ! empty( $removed_caps ) ) {
212 $errors[] = new \WP_Error( 'invalid_configuration', 'When `clone_role` is disabled, TrustedLogin cannot add or remove capabilities.' );
213 }
214 }
215
216 if ( $errors ) {
217 $error_text = array();
218 foreach ( $errors as $error ) {
219 if ( is_wp_error( $error ) ) {
220 $error_text[] = $error->get_error_message();
221 }
222 }
223
224 $exception_text = 'Invalid TrustedLogin Configuration. Learn more at https://www.trustedlogin.com/configuration/';
225 $exception_text .= "\n- " . implode( "\n- ", $error_text );
226
227 throw new Exception( $exception_text, 406 );
228 }
229
230 return true;
231 }
232
233 /**
234 * Returns a timestamp that is the current time + decay time setting
235 *
236 * Note: This is a server timestamp, not a WordPress timestamp
237 *
238 * @param int $decay_time If passed, override the `decay` setting
239 * @param bool $gmt Whether to use server time (false) or GMT time (true). Default: false.
240 *
241 * @return int|false Timestamp in seconds. Default is WEEK_IN_SECONDS from creation (`time()` + 604800). False if no expiration.
242 */
243 public function get_expiration_timestamp( $decay_time = null, $gmt = false ) {
244
245 if ( is_null( $decay_time ) ) {
246 $decay_time = $this->get_setting( 'decay' );
247 }
248
249 if ( 0 === $decay_time ) {
250 return false;
251 }
252
253 $time = current_time( 'timestamp', $gmt );
254
255 return $time + (int) $decay_time;
256 }
257
258 /**
259 * Returns the display name for the vendor; otherwise, the title
260 *
261 * @return string
262 */
263 public function get_display_name() {
264 return $this->get_setting( 'vendor/display_name', $this->get_setting( 'vendor/title', '' ) );
265 }
266
267 /**
268 * Validate and initialize settings array passed to the Client contructor
269 *
270 * @param array|string $config Configuration array or JSON-encoded configuration array
271 *
272 * @return bool|WP_Error[] true: Initialization succeeded; array of WP_Error objects if there are any issues.
273 */
274 protected function parse_settings( $config ) {
275
276 if ( is_string( $config ) ) {
277 $config = json_decode( $config, true );
278 }
279
280 if ( ! is_array( $config ) || empty( $config ) ) {
281 return array( new \WP_Error( 'empty_configuration', 'Configuration array cannot be empty. See https://www.trustedlogin.com/configuration/ for more information.' ) );
282 }
283
284 $defaults = $this->get_default_settings();
285
286 $filtered_config = array_filter( $config, array( $this, 'is_not_null' ) );
287
288 return shortcode_atts( $defaults, $filtered_config );
289 }
290
291 /**
292 * Filter out null input values
293 *
294 * @internal Used for parsing settings
295 *
296 * @param mixed $input Input to test against.
297 *
298 * @return bool True: not null. False: null
299 */
300 public function is_not_null( $input ) {
301 return ! is_null( $input );
302 }
303
304 /**
305 * Gets the default settings for the Client and define dynamic defaults (like paths/css and paths/js)
306 *
307 * @since 1.0.0
308 *
309 * @return array Array of default settings.
310 */
311 public function get_default_settings() {
312
313 $default_settings = $this->default_settings;
314
315 $default_settings['paths']['css'] = plugin_dir_url( __FILE__ ) . 'assets/trustedlogin.css';
316 $default_settings['paths']['js'] = plugin_dir_url( __FILE__ ) . 'assets/trustedlogin.js';
317
318 return $default_settings;
319 }
320
321 /**
322 * @return string Vendor namespace, sanitized with dashes
323 */
324 public function ns() {
325
326 static $namespace;
327
328 if ( ! $namespace ) {
329 $ns = $this->get_setting( 'vendor/namespace' );
330
331 $namespace = sanitize_title_with_dashes( $ns );
332 }
333
334 return $namespace;
335 }
336
337 /**
338 * Helper Function: Get a specific setting or return a default value.
339 *
340 * @since 1.0.0
341 *
342 * @param string $key The setting to fetch, nested results are delimited with forward slashes (eg vendor/name => settings['vendor']['name'])
343 * @param mixed $default - if no setting found or settings not init, return this value.
344 * @param array $settings Pass an array to fetch value for instead of using the default settings array
345 *
346 * @return string|array
347 */
348 public function get_setting( $key, $default = null, $settings = array() ) {
349
350 if ( empty( $settings ) ) {
351 $settings = $this->settings;
352 }
353
354 if ( is_null( $default ) ) {
355 $default = $this->get_multi_array_value( $this->get_default_settings(), $key );
356 }
357
358 if ( empty( $settings ) || ! is_array( $settings ) ) {
359 return $default;
360 }
361
362 return $this->get_multi_array_value( $settings, $key, $default );
363 }
364
365 /**
366 * Returns the full settings array
367 *
368 * @since 1.5.0
369 *
370 * @return array Settings as passed to the constructor.
371 */
372 public function get_settings() {
373 return $this->settings;
374 }
375
376 /**
377 * Gets a specific property value within a multidimensional array.
378 *
379 * @param array $array The array to search in.
380 * @param string $name The name of the property to find.
381 * @param string $default Optional. Value that should be returned if the property is not set or empty. Defaults to null.
382 *
383 * @return null|string|mixed The value
384 */
385 private function get_multi_array_value( $array, $name, $default = null ) {
386
387 if ( ! is_array( $array ) && ! ( is_object( $array ) && $array instanceof ArrayAccess ) ) {
388 return $default;
389 }
390
391 $names = explode( '/', $name );
392 $val = $array;
393 foreach ( $names as $current_name ) {
394 $val = $this->get_array_value( $val, $current_name, $default );
395 }
396
397 return $val;
398 }
399
400 /**
401 * Get a specific property of an array without needing to check if that property exists.
402 *
403 * Provide a default value if you want to return a specific value if the property is not set.
404 *
405 * @param array $array Array from which the property's value should be retrieved.
406 * @param string $prop Name of the property to be retrieved.
407 * @param string $default Optional. Value that should be returned if the property is not set or empty. Defaults to null.
408 *
409 * @return null|string|mixed The value
410 */
411 private function get_array_value( $array, $prop, $default = null ) {
412 if ( ! is_array( $array ) && ! ( is_object( $array ) && $array instanceof ArrayAccess ) ) {
413 return $default;
414 }
415
416 // Directly fetch the value if it exists, otherwise use the default.
417 $value = isset( $array[ $prop ] ) ? $array[ $prop ] : $default;
418
419 // Special handling for zero and false.
420 if ( 0 === $value || false === $value ) {
421 return $value;
422 }
423
424 // If the value is empty and a default is provided, use the default.
425 if ( empty( $value ) && null !== $default ) {
426 return $default;
427 }
428
429 return $value;
430 }
431
432 /**
433 * Checks whether SSL requirements are met.
434 *
435 * @since 1.0.0
436 *
437 * @return bool Whether the vendor-defined SSL requirements are met.
438 */
439 public function meets_ssl_requirement() {
440
441 $return = true;
442
443 if ( $this->get_setting( 'require_ssl', true ) && ! is_ssl() ) {
444 $return = false;
445 }
446
447 /**
448 * @internal Do not rely on this!!!!
449 * @todo Remove this
450 *
451 * @param bool $return Does this site meet the SSL requirement?
452 */
453 return apply_filters( 'trustedlogin/' . $this->ns() . '/meets_ssl_requirement', $return );
454 }
455
456 }
457