PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.3.1
Defender Security – Malware Scanner, Login Security & Firewall v5.3.1
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / extra / hub-connector / inc / class-admin.php

class-admin.php in Defender Security – Malware Scanner, Login Security & Firewall 5.3.1, at extra/hub-connector/inc/class-admin.php

673 lines 19.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The Admin class to process admin side actions.
4 *
5 * @link http://wpmudev.com
6 * @since 1.0.0
7 * @author Joel James <[email protected]>
8 * @package WPMUDEV\Hub\Connector
9 */
10
11 namespace WPMUDEV\Hub\Connector;
12
13 /**
14 * Class Admin
15 */
16 class Admin {
17
18 use Singleton;
19
20 /**
21 * Plugin identifier.
22 *
23 * @var string $plugin
24 */
25 protected $plugin = 'default';
26
27 /**
28 * Admin page screen IDs.
29 *
30 * @var array Screen IDs.
31 */
32 protected $screens = array();
33
34 /**
35 * Extra query arguments for auth URLs.
36 *
37 * @var array Arguments.
38 */
39 protected $extra_args = array();
40
41 /**
42 * Initialize admin class.
43 *
44 * @since 1.0.0
45 */
46 protected function __construct() {
47 // Add required classes to body.
48 add_filter( 'admin_body_class', array( $this, 'add_body_class' ) );
49 // Register UI for a plugin.
50 add_action( 'wpmudev_hub_connector_ui', array( $this, 'render' ) );
51 // Process auth callback from hub.
52 add_action( 'admin_init', array( $this, 'process_auth_callback' ) );
53 // Include WDP ID header.
54 add_filter( 'extra_plugin_headers', array( $this, 'include_wdp_id_header' ) );
55 }
56
57 /**
58 * Get extra query arguments for plugin.
59 *
60 * @since 1.0.0
61 *
62 * @param string $plugin Plugin identifier.
63 *
64 * @return array
65 */
66 public function get_plugin_extra_args( string $plugin ): array {
67 if ( ! empty( $plugin ) && ! empty( $this->extra_args[ $plugin ] ) ) {
68 return $this->extra_args[ $plugin ];
69 }
70
71 return array();
72 }
73
74 /**
75 * Get screen IDs of plugin.
76 *
77 * @since 1.0.0
78 *
79 * @param string $plugin Plugin identifier.
80 *
81 * @return array
82 */
83 public function get_plugin_screens( string $plugin ): array {
84 if ( ! empty( $plugin ) && ! empty( $this->screens[ $plugin ] ) ) {
85 return $this->screens[ $plugin ];
86 }
87
88 return array();
89 }
90
91 /**
92 * Set extra query arguments URL redirects.
93 *
94 * @since 1.0.0
95 *
96 * @param string $plugin Plugin identifier.
97 * @param array $options Options.
98 *
99 * @return void
100 */
101 public function set_plugin_options( string $plugin, array $options = array() ) {
102 if ( empty( $plugin ) ) {
103 return;
104 }
105
106 $options = wp_parse_args(
107 $options,
108 array(
109 'screens' => array(),
110 'extra_args' => array(),
111 )
112 );
113
114 // Set options.
115 $this->set_plugin_screens( $plugin, $options['screens'] );
116 $this->set_plugin_extra_args( $plugin, $options['extra_args'] );
117 }
118
119 /**
120 * Set admin screens to process.
121 *
122 * UI will be rendered only on these screens.
123 *
124 * @since 1.0.0
125 *
126 * @param string $plugin Plugin identifier.
127 * @param array $screens Screen IDs.
128 *
129 * @return void
130 */
131 public function set_plugin_screens( string $plugin, array $screens = array() ) {
132 if ( ! empty( $screens ) ) {
133 foreach ( $screens as $screen ) {
134 $this->screens[ $screen ] = $plugin;
135 }
136 }
137 }
138
139 /**
140 * Set extra query arguments for login and register redirects.
141 *
142 * @since 1.0.0
143 *
144 * @param string $plugin Plugin identifier.
145 * @param array $args Extra arguments.
146 *
147 * @return void
148 */
149 public function set_plugin_extra_args( string $plugin, array $args = array() ) {
150 $this->extra_args[ $plugin ] = $args;
151 }
152
153 /**
154 * Make sure we get WDP ID in plugins data.
155 *
156 * NOTE: We NEED TO keep using WDP ID as the
157 * key, because that's how the WP filter works.
158 *
159 * @since 1.0.0
160 *
161 * @param array $headers Existing headers.
162 *
163 * @return array
164 */
165 public function include_wdp_id_header( $headers ) {
166 // Include WDP ID.
167 $headers[] = 'WDP ID';
168
169 return $headers;
170 }
171
172 /**
173 * Get plugin identifier.
174 *
175 * Get an identifier for plugins to identify if the
176 * module was initialised by their plugin.
177 * If no identifier was being passed from plugin, it will have
178 * a default value "default".
179 *
180 * @since 1.0.0
181 *
182 * @return string
183 */
184 public function get_plugin_id() {
185 return $this->plugin;
186 }
187
188 /**
189 * Render hub connector UI.
190 *
191 * @since 1.0.0
192 *
193 * @param string $plugin Plugin identifier.
194 */
195 public function render( $plugin = 'default' ) {
196 if ( ! empty( $plugin ) ) {
197 $this->plugin = $plugin;
198 }
199
200 echo '<div id="wpmudev-hub-connector"></div>';
201
202 // Enqueue assets.
203 $this->enqueue_assets();
204 }
205
206 /**
207 * Add required classes to HTML body.
208 *
209 * @since 1.0.0
210 *
211 * @param string $classes Class names.
212 */
213 public function add_body_class( $classes ) {
214 if ( $this->is_allowed_screen() ) {
215 $classes .= ' ' . \WPMUDEV_HUB_CONNECTOR_SUI_VERSION;
216 }
217
218 return $classes;
219 }
220
221 /**
222 * Check if current admin page is one of the allowed pages.
223 *
224 * @since 1.0.0
225 *
226 * @return bool
227 */
228 public function is_allowed_screen() {
229 if ( function_exists( 'get_current_screen' ) ) {
230 $screen = get_current_screen();
231 // Allowed screens.
232 $screens = array_keys( $this->screens );
233
234 // Check if current screen found in list.
235 return ! empty( $screen->id ) && in_array( $screen->id, $screens, true );
236 }
237
238 return false;
239 }
240
241 /**
242 * Enqueue styles and scripts for UI.
243 *
244 * This should be called only when required.
245 *
246 * @since 1.0.0
247 */
248 protected function enqueue_assets() {
249 // SUI and custom styles.
250 wp_enqueue_style(
251 'hub-connector',
252 plugin_dir_url( \WPMUDEV_HUB_CONNECTOR_FILE ) . 'assets/css/hub-connector.min.css',
253 array(),
254 \WPMUDEV_HUB_CONNECTOR_VERSION
255 );
256
257 // Script.
258 wp_enqueue_script(
259 'hub-connector',
260 plugin_dir_url( \WPMUDEV_HUB_CONNECTOR_FILE ) . 'assets/js/hub-connector.min.js',
261 array( 'wp-element', 'wp-i18n' ),
262 \WPMUDEV_HUB_CONNECTOR_VERSION,
263 true
264 );
265
266 // Get current URL.
267 $current_url = remove_query_arg(
268 array(
269 'api_error',
270 'set_apikey',
271 'auth_error',
272 'connection_error',
273 'invalid_key',
274 'site_limit_exceeded',
275 'site_limit',
276 'available_hosting_sites',
277 'hub_connector_callback',
278 'is_multi_auth',
279 'user_apikey',
280 'auth_nonce',
281 'type',
282 ),
283 Data::get()->current_url()
284 );
285
286 // Prepare redirect URL.
287 $redirect_url = add_query_arg( array( 'hub_connector_callback' => 1 ), $current_url );
288
289 $auth_nonce = wp_create_nonce( 'auth_nonce' );
290
291 // Extra arguments.
292 $extra_args = $this->get_plugin_extra_args_from_screen();
293
294 // URL arguments for registration URL.
295 $register_args = array(
296 'signup' => 'site-connect',
297 'site_connect_url' => urlencode( add_query_arg( 'auth_nonce', $auth_nonce, $redirect_url ) ),
298 );
299 // Include extra args.
300 if ( ! empty( $extra_args['register'] ) ) {
301 $register_args = array_merge( $register_args, $extra_args['register'] );
302 }
303
304 // Vars for javascript.
305 $vars = array(
306 'api_url' => rest_url( 'hub-connector/v1/' ),
307 'nonce' => wp_create_nonce( 'wp_rest' ),
308 'is_syncing' => false,
309 'is_team_selection' => false,
310 'has_access' => current_user_can( 'manage_options' ),
311 'is_logged_in' => API::get()->is_logged_in(),
312 'current_tab' => isset( $_GET['hub_connector_callback'] ) ? 'login' : 'register',
313 'login' => array(
314 'hub_auth_url' => add_query_arg(
315 $extra_args['auth'] ?? array(),
316 API::get()->rest_url( 'site-authenticate' )
317 ),
318 'team_auth_url' => add_query_arg(
319 $extra_args['team_auth'] ?? array(),
320 API::get()->rest_url( 'site-authenticate-team' )
321 ),
322 'google_auth_url' => add_query_arg(
323 $extra_args['google_auth'] ?? array(),
324 API::get()->rest_url( 'google-auth' )
325 ),
326 'redirect_url' => $redirect_url,
327 'domain' => Data::get()->network_site_url(),
328 'auth_nonce' => $auth_nonce,
329 'current_url' => $current_url,
330 'forgot_url' => add_query_arg(
331 $extra_args['forgot_password'] ?? array(),
332 Data::get()->server_url( 'forgot-password' )
333 ),
334 'register_url' => add_query_arg(
335 $register_args,
336 Data::get()->server_url( 'register' )
337 ),
338 ),
339 'texts' => $this->text_vars(),
340 );
341
342 /**
343 * Filter hook to modify script vars based on actions.
344 *
345 * @since 1.0.0
346 *
347 * @param array $vars Vars.
348 * @param string $plugin Plugin identifier.
349 */
350 $vars = apply_filters( 'wpmudev_hub_connector_localize_vars', $vars, $this->get_plugin_id() );
351
352 // Localized vars.
353 wp_localize_script( 'hub-connector', 'hubConnectorVars', $vars );
354 }
355
356 /**
357 * Process auth callback from the API.
358 *
359 * This method handles the dynamic script vars required
360 * for the UI.
361 *
362 * @since 1.0.0
363 *
364 * @return bool
365 */
366 public function process_auth_callback() {
367 // Only for Hub connector callback.
368 if ( ! isset( $_REQUEST['hub_connector_callback'] ) ) {
369 return false;
370 }
371
372 // Should be capable to perform the actions.
373 if ( ! current_user_can( 'manage_options' ) ) {
374 return $this->update_vars( array( 'has_access' => false ) );
375 }
376
377 // If not logged in.
378 if ( ! API::get()->is_logged_in() ) {
379 $error = $this->get_auth_error();
380 if ( ! empty( $error ) ) {
381 // Set auth errors.
382 return $this->update_vars( array( 'auth_error' => $error ) );
383 }
384
385 // Auth nonce verification.
386 if ( ! $this->verify_nonce() ) {
387 // Failed. So no access.
388 return $this->update_vars( array( 'has_access' => false ) );
389 }
390
391 // Is team selection callback.
392 if ( $this->is_team_selection() ) {
393 // Get the teams for API key.
394 $teams = API::get()->get_hub_teams( trim( $_REQUEST['user_apikey'] ) );
395
396 // Set team selection page vars.
397 return $this->update_vars(
398 array(
399 'api_key' => trim( $_REQUEST['user_apikey'] ),
400 'hub_teams' => $teams,
401 'is_team_selection' => true,
402 )
403 );
404 }
405
406 // Is the set API key page.
407 if ( ! empty( $_REQUEST['set_apikey'] ) ) {
408 // Set API key.
409 API::get()->set_api_key( trim( $_REQUEST['set_apikey'] ) );
410
411 // Make sure to start syncing.
412 return $this->update_vars(
413 array(
414 'is_syncing' => true,
415 'is_logged_in' => true,
416 )
417 );
418 }
419 }
420
421 return false;
422 }
423
424 /**
425 * Get authentication error messages.
426 *
427 * Based on the error code, prepare different error messages.
428 *
429 * @since 1.0.0
430 *
431 * @return string
432 */
433 private function get_auth_error() {
434 $error = '';
435 $reset_url = Data::get()->server_url( 'wp-login.php?action=lostpassword' );
436 $skip_trial_url = Data::get()->server_url( 'hub/account/?skip_trial' );
437 $trial_info_url = Data::get()->server_url( 'docs/getting-started/how-free-trials-work/' );
438 $websites_url = Data::get()->server_url( 'hub2/' );
439 $security_info_url = Data::get()->server_url( 'manuals/hub-security/' );
440 $support_url = Data::get()->server_url( 'hub/support/' );
441 $account_details_url = Data::get()->server_url( 'hub2/account/details/' );
442
443 if ( isset( $_GET['api_error'] ) ) {
444 // Get errors.
445 $api_error = esc_html( $_GET['api_error'] );
446 $auth_error = isset( $_GET['auth_error'] ) ? esc_html( $_GET['auth_error'] ) : '';
447
448 if ( 1 === (int) $api_error || 'auth' === $api_error ) {
449 switch ( $auth_error ) {
450 case 'google_linked':
451 $error = sprintf(
452 // translators: %s Account detail URL.
453 __( 'You are currently using your Google account as your preferred login method. If you wish to login with your WPMU DEV email & password instead, please change the <strong>Login Method</strong> in <a href="%s" target="_blank">your WPMU DEV account</a>.', 'wpmudev' ),
454 $account_details_url
455 );
456 break;
457 case 'google_unlinked':
458 $error = sprintf(
459 // translators: %s Account detail URL.
460 __( 'You are currently using your WPMU DEV email & password as your preferred login method. If you wish to login with your Google account instead, please change the <strong>Login Method</strong> in <a href="%s" target="_blank">your WPMU DEV account</a>.', 'wpmudev' ),
461 $account_details_url
462 );
463 break;
464 case 'reauth_google':
465 $error = sprintf(
466 // translators: %1$s Account detail URL, %2$s Reset URL.
467 __( 'Due to security improvements, you will need to re-link your Google account in the Hub. Please log in with your WPMU DEV email & password for now, then set up your preferred <strong>Login Method</strong> in <a href="%1$s" target="_blank">your WPMU DEV account</a>. Forgot your password? You can <a href="%2$s" target="_blank">reset it here</a>.', 'wpmudev' ),
468 $account_details_url,
469 $reset_url
470 );
471 break;
472 default:
473 // Invalid credentials.
474 $error = sprintf(
475 '%s<br><a href="%s" target="_blank">%s</a>',
476 esc_html__( 'Your login details were incorrect. Please make sure you\'re using your WPMU DEV email and password and try again.', 'wpmudev' ),
477 $reset_url,
478 esc_html__( 'Forgot your password?', 'wpmudev' )
479 );
480 break;
481 }
482 } else {
483 switch ( $api_error ) {
484 case 'in_trial':
485 $error = sprintf(
486 '%s<br><a href="%s" target="_blank">%s</a>',
487 sprintf(
488 // translators: %1$s Rest URL, %2$s Upgrade URL, %3$s Trial URL.
489 __( 'This domain has previously been registered with us by the user %1$s. To use WPMU DEV on this domain, you can either log in with the original account (you can <a target="_blank" href="%2$s">reset your password</a>) or <a target="_blank" href="%3$s">upgrade your trial</a> to a full membership. Trial accounts can\'t use previously registered domains - <a target="_blank" href="%4$s">here\'s why</a>.', 'wpmudev' ),
490 '<strong style="word-break: break-all;">' . esc_html( $_GET['display_name'] ) . '</strong>', // phpcs:ignore
491 $reset_url,
492 $skip_trial_url,
493 $trial_info_url
494 ),
495 $support_url,
496 __( 'Contact support if you need further assistance &raquo;', 'wpmudev' )
497 );
498 break;
499 case 'already_registered':
500 $error = sprintf(
501 // translators: %1$d Account name, %2$s Security info, %3$s Hub URL, %4$s Support URL.
502 __( 'This site is currently registered to %1$s. For <a target="_blank" href="%2$s">security reasons</a> they will need to go to the <a target="_blank" href="%3$s">WPMU DEV Hub</a> and remove this domain before you can log in. If you do not have access to that account, and have no way of contacting that user, please <a target="_blank" href="%4$s">contact support for assistance</a>.', 'wpmudev' ),
503 '<strong style="word-break: break-all;">' . esc_html( $_GET['display_name'] ) . '</strong>', // phpcs:ignore.
504 $security_info_url,
505 $websites_url,
506 $support_url
507 );
508 break;
509 case 'banned_account':
510 $error = sprintf(
511 // translators: %s Support URL.
512 __( 'This domain cannot be registered to your WPMU DEV account.<br><a href="%s">Contact Accounts & Billing if you need further assistance »</a>', 'wpmudev' ),
513 Data::get()->server_url( 'hub2/#ask-question' )
514 );
515 break;
516 case 'invalid_nonce':
517 case 'invalid_double_submit_cookie':
518 case 'invalid_google_creds':
519 case '':
520 $error = __( 'Google login failed. Please try again.', 'wpmudev' );
521 break;
522 default:
523 // This in case we add new error types in the future.
524 $error = __( 'Unknown error. Please update the WPMU DEV Dashboard plugin and try again.', 'wpmudev' );
525 break;
526 }
527 }
528 } elseif ( ! empty( $_REQUEST['connection_error'] ) ) {
529 // Variable `$connection_error` is set by the UI function `render_dashboard`.
530 $error = sprintf(
531 '%s<br>%s<br><em>%s</em>',
532 __( 'Your server had a problem connecting to WPMU DEV. Please try again.', 'wpmudev' ),
533 __( 'If this problem continues, please contact your host with this error message and ask:', 'wpmudev' ),
534 sprintf(
535 // translators: url to API.
536 __( '"Is PHP on my server properly configured to be able to contact %s with a POST HTTP request via fsockopen or CURL?"', 'wpmudev' ),
537 Data::get()->server_url()
538 )
539 );
540 } elseif ( ! empty( $_REQUEST['invalid_key'] ) ) {
541 // Invalid API key.
542 $error = __( 'Your API Key was invalid. Please try again.', 'wpmudev' );
543 }
544
545 /**
546 * Filter to modify auth error text.
547 *
548 * @since 1.0.0
549 *
550 * @param string $error Error message.
551 * @param string $plugin Plugin identifier.
552 */
553 return apply_filters( 'wpmudev_hub_connector_get_auth_error', $error, $this->get_plugin_id() );
554 }
555
556 /**
557 * Check if current request is team selection after auth.
558 *
559 * @since 1.0.0
560 *
561 * @return bool
562 */
563 private function is_team_selection() {
564 return (
565 // Should have multi auth param.
566 isset( $_REQUEST['is_multi_auth'] )
567 && 1 === (int) $_REQUEST['is_multi_auth']
568 // Should have an API key.
569 && ! empty( $_REQUEST['user_apikey'] )
570 );
571 }
572
573 /**
574 * Verify auth nonce.
575 *
576 * After or during the hub auth process, we need to make sure
577 * the nonce is valid.
578 *
579 * @since 1.0.0
580 *
581 * @return bool
582 */
583 private function verify_nonce() {
584 return wp_verify_nonce( ( isset( $_REQUEST['auth_nonce'] ) ? $_REQUEST['auth_nonce'] : '' ), 'auth_nonce' );
585 }
586
587 /**
588 * Script vars to assign.
589 *
590 * @since 1.0.0
591 *
592 * @param array $data Script vars.
593 *
594 * @return bool
595 */
596 private function update_vars( $data ) {
597 // Update the script vars array.
598 add_filter(
599 'wpmudev_hub_connector_localize_vars',
600 function ( $vars ) use ( $data ) {
601 return array_merge( $vars, $data );
602 }
603 );
604
605 return true;
606 }
607
608 /**
609 * Localized text strings for UI.
610 *
611 * @since 1.0.0
612 *
613 * @return array
614 */
615 private function text_vars() {
616 $strings = array(
617 'login_title' => __( 'Let’s connect your site', 'wpmudev' ),
618 'login_desc' => __( 'To manage your site from The Hub, log in with your WPMU DEV account email and password.', 'wpmudev' ),
619 'sync_error' => __( 'Could not sync with Hub. Please try again.', 'wpmudev' ),
620 'sync_desc1' => __( 'The Hub connects WPMU DEV to your website and unlocks all the power of our all-in-one platform services.', 'wpmudev' ),
621 'sync_desc2' => __( 'Once your website is connected to the Hub, you will be able to perform updates, managing services - all from one place.', 'wpmudev' ),
622 'sync_loading' => __( 'Please wait a few moments while we connect your website.', 'wpmudev' ),
623 'team_title' => __( 'Choose The Hub Team', 'wpmudev' ),
624 'team_desc' => __( 'We\'ve noticed that you are a member of multiple teams in The Hub. Which team would you like to connect to this site?', 'wpmudev' ),
625 'team_error' => __( 'Unknown API error occurred. Please try again.', 'wpmudev' ),
626 'create_account_desc' => __( "Create a free account to connect your site to WPMU DEV and activate cloud-based link checker. <i>It's fast, seamless, and free.</i>", 'wpmudev' ),
627 'no_access_desc' => __( 'You do not have the permission to view this page.', 'wpmudev' ),
628 'signin_with_google' => __( 'Sign in with Google', 'wpmudev' ),
629 'forgot_password' => __( 'Forgot password?', 'wpmudev' ),
630 'keep_me_logged_in' => __( 'Keep me logged in', 'wpmudev' ),
631 'login' => __( 'Login', 'wpmudev' ),
632 'new_user' => __( 'I am a new user', 'wpmudev' ),
633 'existing_user' => __( 'I am an existing user', 'wpmudev' ),
634 'continue_with_email' => __( 'OR continue with email', 'wpmudev' ),
635 'create_free_account' => __( 'Create a free account', 'wpmudev' ),
636 );
637
638 /**
639 * Filter hook to modify text string vars.
640 *
641 * @since 1.0.0
642 *
643 * @param array $strings Vars.
644 * @param string $plugin Plugin identifier.
645 */
646 return apply_filters( 'wpmudev_hub_connector_localize_text_vars', $strings, $this->get_plugin_id() );
647 }
648
649 /**
650 * Get extra arguments for plugin.
651 *
652 * @return array
653 */
654 private function get_plugin_extra_args_from_screen(): array {
655 $screen = get_current_screen();
656 // We need screen ID.
657 if ( empty( $screen->id ) ) {
658 return array();
659 }
660
661 $screens = $this->screens;
662 // Current screen is not allowed.
663 if ( ! isset( $screens[ $screen->id ] ) ) {
664 return array();
665 }
666
667 // Get plugin ID.
668 $plugin_id = $screens[ $screen->id ];
669 // Get plugin's extra args.
670 return $this->get_plugin_extra_args( $plugin_id );
671 }
672 }
673