File: /var/www/html/wptoho/wp-content/plugins/defender-security/extra/hub-connector/inc/class-admin.php
<?php
/**
* The Admin class to process admin side actions.
*
* @link http://wpmudev.com
* @since 1.0.0
* @author Joel James <joel@incsub.com>
* @package WPMUDEV\Hub\Connector
*/
namespace WPMUDEV\Hub\Connector;
/**
* Class Admin
*/
class Admin {
use Singleton;
/**
* Plugin identifier.
*
* @var string $plugin
*/
protected $plugin = 'default';
/**
* Admin page screen IDs.
*
* @var array Screen IDs.
*/
protected $screens = array();
/**
* Extra query arguments for auth URLs.
*
* @var array Arguments.
*/
protected $extra_args = array();
/**
* Initialize admin class.
*
* @since 1.0.0
*/
protected function __construct() {
// Add required classes to body.
add_filter( 'admin_body_class', array( $this, 'add_body_class' ) );
// Register UI for a plugin.
add_action( 'wpmudev_hub_connector_ui', array( $this, 'render' ) );
// Process auth callback from hub.
add_action( 'admin_init', array( $this, 'process_auth_callback' ) );
// Include WDP ID header.
add_filter( 'extra_plugin_headers', array( $this, 'include_wdp_id_header' ) );
}
/**
* Get extra query arguments for plugin.
*
* @since 1.0.0
*
* @param string $plugin Plugin identifier.
*
* @return array
*/
public function get_plugin_extra_args( string $plugin ): array {
if ( ! empty( $plugin ) && ! empty( $this->extra_args[ $plugin ] ) ) {
return $this->extra_args[ $plugin ];
}
return array();
}
/**
* Get screen IDs of plugin.
*
* @since 1.0.0
*
* @param string $plugin Plugin identifier.
*
* @return array
*/
public function get_plugin_screens( string $plugin ): array {
if ( ! empty( $plugin ) && ! empty( $this->screens[ $plugin ] ) ) {
return $this->screens[ $plugin ];
}
return array();
}
/**
* Set extra query arguments URL redirects.
*
* @since 1.0.0
*
* @param string $plugin Plugin identifier.
* @param array $options Options.
*
* @return void
*/
public function set_plugin_options( string $plugin, array $options = array() ) {
if ( empty( $plugin ) ) {
return;
}
$options = wp_parse_args(
$options,
array(
'screens' => array(),
'extra_args' => array(),
)
);
// Set options.
$this->set_plugin_screens( $plugin, $options['screens'] );
$this->set_plugin_extra_args( $plugin, $options['extra_args'] );
}
/**
* Set admin screens to process.
*
* UI will be rendered only on these screens.
*
* @since 1.0.0
*
* @param string $plugin Plugin identifier.
* @param array $screens Screen IDs.
*
* @return void
*/
public function set_plugin_screens( string $plugin, array $screens = array() ) {
if ( ! empty( $screens ) ) {
foreach ( $screens as $screen ) {
$this->screens[ $screen ] = $plugin;
}
}
}
/**
* Set extra query arguments for login and register redirects.
*
* @since 1.0.0
*
* @param string $plugin Plugin identifier.
* @param array $args Extra arguments.
*
* @return void
*/
public function set_plugin_extra_args( string $plugin, array $args = array() ) {
$this->extra_args[ $plugin ] = $args;
}
/**
* Make sure we get WDP ID in plugins data.
*
* NOTE: We NEED TO keep using WDP ID as the
* key, because that's how the WP filter works.
*
* @since 1.0.0
*
* @param array $headers Existing headers.
*
* @return array
*/
public function include_wdp_id_header( $headers ) {
// Include WDP ID.
$headers[] = 'WDP ID';
return $headers;
}
/**
* Get plugin identifier.
*
* Get an identifier for plugins to identify if the
* module was initialised by their plugin.
* If no identifier was being passed from plugin, it will have
* a default value "default".
*
* @since 1.0.0
*
* @return string
*/
public function get_plugin_id() {
return $this->plugin;
}
/**
* Render hub connector UI.
*
* @since 1.0.0
*
* @param string $plugin Plugin identifier.
*/
public function render( $plugin = 'default' ) {
if ( ! empty( $plugin ) ) {
$this->plugin = $plugin;
}
echo '<div id="wpmudev-hub-connector"></div>';
// Enqueue assets.
$this->enqueue_assets();
}
/**
* Add required classes to HTML body.
*
* @since 1.0.0
*
* @param string $classes Class names.
*/
public function add_body_class( $classes ) {
if ( $this->is_allowed_screen() ) {
$classes .= ' ' . \WPMUDEV_HUB_CONNECTOR_SUI_VERSION;
}
return $classes;
}
/**
* Check if current admin page is one of the allowed pages.
*
* @since 1.0.0
*
* @return bool
*/
public function is_allowed_screen() {
if ( function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
// Allowed screens.
$screens = array_keys( $this->screens );
// Check if current screen found in list.
return ! empty( $screen->id ) && in_array( $screen->id, $screens, true );
}
return false;
}
/**
* Enqueue styles and scripts for UI.
*
* This should be called only when required.
*
* @since 1.0.0
*/
protected function enqueue_assets() {
// SUI and custom styles.
wp_enqueue_style(
'hub-connector',
plugin_dir_url( \WPMUDEV_HUB_CONNECTOR_FILE ) . 'assets/css/hub-connector.min.css',
array(),
\WPMUDEV_HUB_CONNECTOR_VERSION
);
// Script.
wp_enqueue_script(
'hub-connector',
plugin_dir_url( \WPMUDEV_HUB_CONNECTOR_FILE ) . 'assets/js/hub-connector.min.js',
array( 'wp-element', 'wp-i18n' ),
\WPMUDEV_HUB_CONNECTOR_VERSION,
true
);
// Get current URL.
$current_url = remove_query_arg(
array(
'api_error',
'set_apikey',
'auth_error',
'connection_error',
'invalid_key',
'site_limit_exceeded',
'site_limit',
'available_hosting_sites',
'hub_connector_callback',
'is_multi_auth',
'user_apikey',
'auth_nonce',
'type',
),
Data::get()->current_url()
);
// Prepare redirect URL.
$redirect_url = add_query_arg( array( 'hub_connector_callback' => 1 ), $current_url );
$auth_nonce = wp_create_nonce( 'auth_nonce' );
// Extra arguments.
$extra_args = $this->get_plugin_extra_args_from_screen();
// URL arguments for registration URL.
$register_args = array(
'signup' => 'site-connect',
'site_connect_url' => rawurlencode( add_query_arg( 'auth_nonce', $auth_nonce, $redirect_url ) ),
);
// Include extra args.
if ( ! empty( $extra_args['register'] ) ) {
$register_args = array_merge( $register_args, $extra_args['register'] );
}
// Vars for javascript.
$vars = array(
'api_url' => rest_url( 'hub-connector/v1/' ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'is_syncing' => false,
'is_team_selection' => false,
'has_access' => current_user_can( 'manage_options' ),
'is_logged_in' => API::get()->is_logged_in(),
// Not being used for literal output / DB insert.
// phpcs:disable WordPress.Security.NonceVerification.Recommended
'current_tab' => isset( $_GET['hub_connector_callback'] ) ? 'login' : 'register',
// phpcs:enable WordPress.Security.NonceVerification.Recommended
'login' => array(
'hub_auth_url' => add_query_arg(
$extra_args['auth'] ?? array(),
API::get()->rest_url( 'site-authenticate' )
),
'team_auth_url' => add_query_arg(
$extra_args['team_auth'] ?? array(),
API::get()->rest_url( 'site-authenticate-team' )
),
'google_auth_url' => add_query_arg(
$extra_args['google_auth'] ?? array(),
API::get()->rest_url( 'google-auth' )
),
'redirect_url' => $redirect_url,
'domain' => Data::get()->network_site_url(),
'auth_nonce' => $auth_nonce,
'current_url' => $current_url,
'forgot_url' => add_query_arg(
$extra_args['forgot_password'] ?? array(),
Data::get()->server_url( 'forgot-password' )
),
'register_url' => add_query_arg(
$register_args,
Data::get()->server_url( 'register' )
),
),
'texts' => $this->text_vars(),
);
/**
* Filter hook to modify script vars based on actions.
*
* @since 1.0.0
*
* @param array $vars Vars.
* @param string $plugin Plugin identifier.
*/
$vars = apply_filters( 'wpmudev_hub_connector_localize_vars', $vars, $this->get_plugin_id() );
// Localized vars.
wp_localize_script( 'hub-connector', 'hubConnectorVars', $vars );
}
/**
* Process auth callback from the API.
*
* This method handles the dynamic script vars required
* for the UI.
*
* @since 1.0.0
*
* @return bool
*/
public function process_auth_callback() {
// we do verify nonce, but we do have more sanity checks to be done first.
// phpcs:disable WordPress.Security.NonceVerification.Recommended
// Only for Hub connector callback.
if ( ! isset( $_REQUEST['hub_connector_callback'] ) ) {
return false;
}
// Should be capable to perform the actions.
if ( ! current_user_can( 'manage_options' ) ) {
return $this->update_vars( array( 'has_access' => false ) );
}
// If not logged in.
if ( ! API::get()->is_logged_in() ) {
// Auth nonce verification.
if ( ! $this->verify_nonce() ) {
// Failed. So no access.
return $this->update_vars( array( 'has_access' => false ) );
}
$error = $this->get_auth_error();
if ( ! empty( $error ) ) {
// Set auth errors.
return $this->update_vars( array( 'auth_error' => $error ) );
}
// Is team selection callback.
if ( $this->is_team_selection() ) {
$user_api_key = trim( sanitize_key( wp_unslash( $_REQUEST['user_apikey'] ?? '' ) ) );
// Get the teams for API key.
$teams = API::get()->get_hub_teams( $user_api_key );
// Set team selection page vars.
return $this->update_vars(
array(
'api_key' => $user_api_key,
'hub_teams' => $teams,
'is_team_selection' => true,
)
);
}
// Is the set API key page.
if ( ! empty( $_REQUEST['set_apikey'] ) ) {
$set_api_key = trim( sanitize_key( wp_unslash( $_REQUEST['set_apikey'] ) ) );
// Set API key.
API::get()->set_api_key( $set_api_key );
// Make sure to start syncing.
return $this->update_vars(
array(
'is_syncing' => true,
'is_logged_in' => true,
)
);
}
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
return false;
}
/**
* Get authentication error messages.
*
* Based on the error code, prepare different error messages.
*
* @since 1.0.0
*
* @return string
*/
private function get_auth_error() {
/**
* Nonce is verified in `process_auth_callback` before this method called.
*
* @see self::process_auth_callback()
*/
// phpcs:disable WordPress.Security.NonceVerification.Recommended
$error = '';
$reset_url = Data::get()->server_url( 'wp-login.php?action=lostpassword' );
$skip_trial_url = Data::get()->server_url( 'hub/account/?skip_trial' );
$trial_info_url = Data::get()->server_url( 'docs/getting-started/how-free-trials-work/' );
$websites_url = Data::get()->server_url( 'hub2/' );
$security_info_url = Data::get()->server_url( 'manuals/hub-security/' );
$support_url = Data::get()->server_url( 'hub/support/' );
$account_details_url = Data::get()->server_url( 'hub2/account/details/' );
if ( isset( $_GET['api_error'] ) ) {
// Get errors.
$api_error = sanitize_key( wp_unslash( $_GET['api_error'] ) );
$auth_error = sanitize_key( wp_unslash( $_GET['auth_error'] ?? '' ) );
if ( 1 === (int) $api_error || 'auth' === $api_error ) {
switch ( $auth_error ) {
case 'google_linked':
$error = sprintf(
// translators: %s Account detail URL.
__(
'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'
),
$account_details_url
);
break;
case 'google_unlinked':
$error = sprintf(
// translators: %s Account detail URL.
__(
'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'
),
$account_details_url
);
break;
case 'reauth_google':
$error = sprintf(
// translators: %1$s Account detail URL, %2$s Reset URL.
__(
'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'
),
$account_details_url,
$reset_url
);
break;
default:
// Invalid credentials.
$error = sprintf(
'%s<br><a href="%s" target="_blank">%s</a>',
esc_html__( 'Your login details were incorrect. Please make sure you\'re using your WPMU DEV email and password and try again.', 'wpmudev' ),
$reset_url,
esc_html__( 'Forgot your password?', 'wpmudev' )
);
break;
}
} else {
switch ( $api_error ) {
case 'in_trial':
$error = sprintf(
'%s<br><a href="%s" target="_blank">%s</a>',
sprintf(
// translators: %1$s Rest URL, %2$s Upgrade URL, %3$s Trial URL.
__(
'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'
),
'<strong style="word-break: break-all;">' . esc_html( $_GET['display_name'] ) . '</strong>', // phpcs:ignore
$reset_url,
$skip_trial_url,
$trial_info_url
),
$support_url,
__( 'Contact support if you need further assistance »', 'wpmudev' )
);
break;
case 'already_registered':
$error = sprintf(
// translators: %1$d Account name, %2$s Security info, %3$s Hub URL, %4$s Support URL.
__(
'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'
),
'<strong style="word-break: break-all;">' . esc_html( $_GET['display_name'] ) . '</strong>', // phpcs:ignore.
$security_info_url,
$websites_url,
$support_url
);
break;
case 'banned_account':
$error = sprintf(
// translators: %s Support URL.
__( 'This domain cannot be registered to your WPMU DEV account.<br><a href="%s">Contact Accounts & Billing if you need further assistance »</a>', 'wpmudev' ),
Data::get()->server_url( 'hub2/#ask-question' )
);
break;
case 'invalid_nonce':
case 'invalid_double_submit_cookie':
case 'invalid_google_creds':
case '':
$error = __( 'Google login failed. Please try again.', 'wpmudev' );
break;
default:
// This in case we add new error types in the future.
$error = __( 'Unknown error. Please update the WPMU DEV Dashboard plugin and try again.', 'wpmudev' );
break;
}
}
} elseif ( ! empty( $_REQUEST['connection_error'] ) ) {
// Variable `$connection_error` is set by the UI function `render_dashboard`.
$error = sprintf(
'%s<br>%s<br><em>%s</em>',
__( 'Your server had a problem connecting to WPMU DEV. Please try again.', 'wpmudev' ),
__( 'If this problem continues, please contact your host with this error message and ask:', 'wpmudev' ),
sprintf(
// translators: url to API.
__( '"Is PHP on my server properly configured to be able to contact %s with a POST HTTP request via fsockopen or CURL?"', 'wpmudev' ),
Data::get()->server_url()
)
);
} elseif ( ! empty( $_REQUEST['invalid_key'] ) ) {
// Invalid API key.
$error = __( 'Your API Key was invalid. Please try again.', 'wpmudev' );
}
/**
* Filter to modify auth error text.
*
* @since 1.0.0
*
* @param string $error Error message.
* @param string $plugin Plugin identifier.
*/
return apply_filters( 'wpmudev_hub_connector_get_auth_error', $error, $this->get_plugin_id() );
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
/**
* Check if current request is team selection after auth.
*
* @since 1.0.0
*
* @return bool
*/
private function is_team_selection() {
/**
* Nonce is verified in `process_auth_callback` before this method called.
*
* @see self::process_auth_callback()
*/
// phpcs:disable WordPress.Security.NonceVerification.Recommended
return (
// Should have multi auth param.
isset( $_REQUEST['is_multi_auth'] )
&& 1 === (int) $_REQUEST['is_multi_auth']
// Should have an API key.
&& ! empty( $_REQUEST['user_apikey'] )
);
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
/**
* Verify auth nonce.
*
* After or during the hub auth process, we need to make sure
* the nonce is valid.
*
* @since 1.0.0
*
* @return bool
*/
private function verify_nonce() {
return wp_verify_nonce( ( sanitize_text_field( wp_unslash( $_REQUEST['auth_nonce'] ?? '' ) ) ), 'auth_nonce' );
}
/**
* Script vars to assign.
*
* @since 1.0.0
*
* @param array $data Script vars.
*
* @return bool
*/
private function update_vars( $data ) {
// Update the script vars array.
add_filter(
'wpmudev_hub_connector_localize_vars',
function ( $vars ) use ( $data ) {
return array_merge( $vars, $data );
}
);
return true;
}
/**
* Localized text strings for UI.
*
* @since 1.0.0
*
* @return array
*/
private function text_vars() {
$strings = array(
'login_title' => __( 'Let’s connect your site', 'wpmudev' ),
'login_desc' => __( 'To manage your site from The Hub, log in with your WPMU DEV account email and password.', 'wpmudev' ),
'sync_error' => __( 'Could not sync with Hub. Please try again.', 'wpmudev' ),
'sync_desc1' => __( 'The Hub connects WPMU DEV to your website and unlocks all the power of our all-in-one platform services.', 'wpmudev' ),
'sync_desc2' => __( 'Once your website is connected to the Hub, you will be able to perform updates, managing services - all from one place.', 'wpmudev' ),
'sync_loading' => __( 'Please wait a few moments while we connect your website.', 'wpmudev' ),
'team_title' => __( 'Choose The Hub Team', 'wpmudev' ),
'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' ),
'team_error' => __( 'Unknown API error occurred. Please try again.', 'wpmudev' ),
'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' ),
'no_access_desc' => __( 'You do not have the permission to view this page.', 'wpmudev' ),
'signin_with_google' => __( 'Sign in with Google', 'wpmudev' ),
'forgot_password' => __( 'Forgot password?', 'wpmudev' ),
'keep_me_logged_in' => __( 'Keep me logged in', 'wpmudev' ),
'login' => __( 'Login', 'wpmudev' ),
'new_user' => __( 'I am a new user', 'wpmudev' ),
'existing_user' => __( 'I am an existing user', 'wpmudev' ),
'continue_with_email' => __( 'OR continue with email', 'wpmudev' ),
'create_free_account' => __( 'Create a free account', 'wpmudev' ),
);
/**
* Filter hook to modify text string vars.
*
* @since 1.0.0
*
* @param array $strings Vars.
* @param string $plugin Plugin identifier.
*/
return apply_filters( 'wpmudev_hub_connector_localize_text_vars', $strings, $this->get_plugin_id() );
}
/**
* Get extra arguments for plugin.
*
* @return array
*/
private function get_plugin_extra_args_from_screen(): array {
$screen = get_current_screen();
// We need screen ID.
if ( empty( $screen->id ) ) {
return array();
}
$screens = $this->screens;
// Current screen is not allowed.
if ( ! isset( $screens[ $screen->id ] ) ) {
return array();
}
// Get plugin ID.
$plugin_id = $screens[ $screen->id ];
// Get plugin's extra args.
return $this->get_plugin_extra_args( $plugin_id );
}
}