matcher = ITSEC_Modules::get_container()->get( Matcher::class );
$this->load_helper();
( new Application_Passwords_Core() )->run();
}
public static function get_instance() {
if ( ! self::$instance ) {
self::$instance = new self;
}
return self::$instance;
}
private function load_helper() {
if ( ! isset( $this->helper ) ) {
require_once( dirname( __FILE__ ) . '/class-itsec-two-factor-helper.php' );
$this->helper = ITSEC_Two_Factor_Helper::get_instance();
}
}
/**
* Register verbs for Sync.
*
* @since 3.6.0
*
* @param Ithemes_Sync_API Sync API object.
*/
public function register_sync_verbs( $api ) {
$api->register( 'itsec-get-two-factor-users', 'Ithemes_Sync_Verb_ITSEC_Get_Two_Factor_Users', dirname( __FILE__ ) . '/sync-verbs/itsec-get-two-factor-users.php' );
$api->register( 'itsec-override-two-factor-user', 'Ithemes_Sync_Verb_ITSEC_Override_Two_Factor_User', dirname( __FILE__ ) . '/sync-verbs/itsec-override-two-factor-user.php' );
$api->register( 'itsec-authorize-two-factor-user', 'Ithemes_Sync_Verb_ITSEC_Authorize_Two_Factor_User', dirname( __FILE__ ) . '/sync-verbs/itsec-authorize-two-factor-user.php' );
}
/**
* Filter to add verbs to the response for the itsec-get-everything verb.
*
* @since 3.6.0
*
* @param array Array of verbs.
*
* @return array Array of verbs.
*/
public function register_sync_get_everything_verbs( $verbs ) {
$verbs['two_factor'][] = 'itsec-get-two-factor-users';
return $verbs;
}
/**
* Add user profile fields.
*
* This executes during the `show_user_profile` & `edit_user_profile` actions.
*
* @param WP_User $user WP_User object of the logged-in user.
*/
public function user_two_factor_options( $user ) {
$this->load_helper();
$allowed_providers = $this->get_allowed_provider_instances_for_user( $user );
if ( ! $allowed_providers ) {
return;
}
$enabled_providers = get_user_meta( $user->ID, $this->_enabled_providers_user_meta_key, true );
if ( ! $enabled_providers ) {
$enabled_providers = array();
}
$primary_provider = get_user_meta( $user->ID, $this->_provider_user_meta_key, true );
wp_nonce_field( 'user_two_factor_options', '_nonce_user_two_factor_options', false );
?>
load_helper();
if ( isset( $_POST['_nonce_user_two_factor_options'] ) ) {
check_admin_referer( 'user_two_factor_options', '_nonce_user_two_factor_options' );
$providers = $this->helper->get_enabled_provider_instances();
// If there are no providers enabled for the site, then let's not worry about this.
if ( empty( $providers ) ) {
return;
}
$enabled_providers = isset( $_POST[ $this->_enabled_providers_user_meta_key ] ) ? $_POST[ $this->_enabled_providers_user_meta_key ] : array();
$this->set_enabled_providers_for_user( $enabled_providers, $user_id );
// Whitelist the new values to only the available classes and empty.
$primary_provider = isset( $_POST[ $this->_provider_user_meta_key ] ) ? $_POST[ $this->_provider_user_meta_key ] : '';
$this->set_primary_provider_for_user( $primary_provider, $user_id );
}
}
/**
* Block XML-RPC login requests for users using Two-Factor.
*
* @param WP_User|WP_Error|null $user_or_error
*
* @return WP_Error|WP_User|null
*/
public function block_xmlrpc( $user_or_error ) {
global $wp_rest_application_password_status;
if ( ! defined( 'XMLRPC_REQUEST' ) || ! XMLRPC_REQUEST ) {
return $user_or_error;
}
if ( ! $user_or_error instanceof WP_User ) {
return $user_or_error;
}
if ( ! $this->is_user_using_two_factor( $user_or_error->ID ) ) {
return $user_or_error;
}
if ( $wp_rest_application_password_status instanceof WP_User && $wp_rest_application_password_status->ID === $user_or_error->ID ) {
return $user_or_error;
}
return new WP_Error( 'two_factor_required', esc_html__( 'User has Two-Factor enabled.', 'it-l10n-ithemes-security-pro' ) );
}
/**
* Update the list of enabled Two Factor providers for a user.
*
* @param array $enabled_providers
* @param int|null $user_id
*
* @return bool
*/
public function set_enabled_providers_for_user( $enabled_providers, $user_id = null ) {
$this->load_helper();
$providers = $this->helper->get_enabled_providers();
// If there are no providers enabled for the site, then let's not worry about this.
if ( empty( $providers ) ) {
return false;
}
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
if ( ! is_array( $enabled_providers ) ) {
// Make sure enabled providers is an array
$enabled_providers = array();
} else {
// Only site-enabled providers can be enabled for a user
$enabled_providers = array_intersect( $enabled_providers, array_keys( $providers ) );
}
return (bool) update_user_meta( $user_id, $this->_enabled_providers_user_meta_key, $enabled_providers );
}
/**
* Set the primary provider for a user.
*
* @param string $primary_provider
* @param int|null $user_id
*
* @return bool
*/
public function set_primary_provider_for_user( $primary_provider, $user_id = null ) {
$this->load_helper();
$providers = $this->helper->get_enabled_providers();
// If there are no providers enabled for the site, then let's not worry about this.
if ( ! $providers ) {
return false;
}
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
if ( empty( $primary_provider ) || array_key_exists( $primary_provider, $providers ) ) {
return (bool) update_user_meta( $user_id, $this->_provider_user_meta_key, $primary_provider );
}
return false;
}
/**
* Get a list of the allowed providers for a user.
*
* @param WP_User $user
*
* @return string[]
*/
public function get_allowed_providers_for_user( $user = null ) {
if ( ! $user instanceof WP_User ) {
$user = wp_get_current_user();
}
if ( ! $user instanceof WP_User || ! $user->exists() ) {
return array();
}
$this->load_helper();
$providers = array_keys( $this->helper->get_enabled_providers() );
/**
* Filter the list of allowed providers for a user.
*
* @param string[] $providers Provider classes.
* @param WP_User $user
*/
return apply_filters( 'itsec_two_factor_allowed_providers_for_user', $providers, $user );
}
/**
* Get the allowed provider instances for a given user.
*
* @param WP_User|null $user
*
* @return Two_Factor_Provider[]
*/
public function get_allowed_provider_instances_for_user( $user = null ) {
$classes = $this->get_allowed_providers_for_user( $user );
$instances = array();
foreach ( $classes as $class ) {
if ( $provider = $this->helper->get_provider_instance( $class ) ) {
$instances[ $class ] = $provider;
}
}
return $instances;
}
/**
* Get all Two-Factor Auth providers that are enabled for the specified|current user.
*
* @param WP_User $user WP_User object of the logged-in user.
*
* @return string[]
*/
public function get_enabled_providers_for_user( $user = null ) {
$this->load_helper();
if ( ! $user instanceof WP_User ) {
$user = wp_get_current_user();
}
$allowed = $this->get_allowed_providers_for_user( $user );
$enabled = get_user_meta( $user->ID, $this->_enabled_providers_user_meta_key, true );
if ( ! $enabled ) {
$enabled = array();
}
$enabled = array_intersect( $enabled, $allowed );
return $enabled;
}
/**
* Get all Two-Factor Auth providers that are both enabled and configured for the specified|current user.
*
* @param WP_User $user WP_User object of the logged-in user.
* @param bool $add_enforced Whether to add in the email provider if 2fa is enforced for the user's account.
*
* @return Two_Factor_Provider[]
*/
public function get_available_providers_for_user( $user = null, $add_enforced = true ) {
$this->load_helper();
if ( ! $user instanceof WP_User ) {
$user = wp_get_current_user();
}
if ( ! $user instanceof WP_User || ! $user->exists() ) {
return array();
}
$enabled = $this->get_enabled_providers_for_user( $user );
$configured = array();
foreach ( $enabled as $classname ) {
$provider = $this->helper->get_provider_instance( $classname );
if ( $provider && $provider->is_available_for_user( $user ) ) {
$configured[ $classname ] = $provider;
}
}
if (
$add_enforced &&
! isset( $configured['Two_Factor_Email'] ) &&
array_key_exists( 'Two_Factor_Email', $this->helper->get_enabled_providers() ) &&
$this->user_requires_two_factor( $user->ID )
) {
$configured['Two_Factor_Email'] = $this->helper->get_provider_instance( 'Two_Factor_Email' );
}
/**
* Filters all of the available providers for a given user.
*
* @param string $configured
* @param WP_User $user
* @param bool $add_enforced
*/
return apply_filters( 'itsec_two_factor_available_providers_for_user', $configured, $user, $add_enforced );
}
/**
* Get the reason that two factor is required for a given user.
*
* 'user_type' - Required because all users are required, their role requires it, or they are a privileged user.
* 'vulnerable_users' - Required because they have a weak password.
* 'vulnerable_site' - Required because the site is running outdated versions of plugins.
*
* @param int|null $user_id
*
* @return string|null|false
*/
public function get_two_factor_requirement_reason( $user_id = null ) {
$this->load_helper();
if ( empty( $user_id ) || ! is_numeric( $user_id ) ) {
$user_id = get_current_user_id();
}
$providers = $this->helper->get_enabled_provider_instances();
if ( ! isset( $providers['Two_Factor_Email'] ) ) {
// Two-factor can't be a requirement if the Email method is not available.
return false;
}
$user = get_userdata( $user_id );
if ( ! $user instanceof WP_User ) {
return false;
}
/**
* Filters the reason that Two-Factor is required for a user.
*
* @param string|null $reason
* @param WP_User $user
*/
return apply_filters( 'itsec_two_factor_requirement_reason', null, $user );
}
/**
* Is the user excluded from Two-Factor authentication.
*
* @param int|WP_User|string $user
*
* @return bool
*/
public function is_user_excluded( $user ) {
if ( ! $user = ITSEC_Lib::get_user( $user ) ) {
return false;
}
$groups = ITSEC_Modules::get_setting( 'two-factor', 'exclude_group' );
return $this->matcher->matches( User_Groups\Match_Target::for_user( $user ), $groups );
}
/**
* Get a description for the reason Two Factor is required.
*
* @param string $reason
*
* @return string
*/
public function get_reason_description( $reason ) {
/**
* Filters the description for the reason Two-Factor is required.
*
* @param string $reason
* @param string $description
*/
return apply_filters( 'itsec_two_factor_requirement_reason_description', $reason, '' );
}
/**
* Does the given user require Two Factor to be enabled.
*
* @param int|null $user_id
*
* @return bool
*/
public function user_requires_two_factor( $user_id = null ) {
$reason = $this->get_two_factor_requirement_reason( $user_id );
return (bool) $reason;
}
/**
* Gets the Two-Factor Auth provider for the specified|current user.
*
* @param int $user_id Optional. User ID. Default is 'null'.
*
* @return Two_Factor_Provider|null
*/
public function get_primary_provider_for_user( $user_id = null ) {
$this->load_helper();
if ( ! $user_id || ! is_numeric( $user_id ) ) {
$user_id = get_current_user_id();
}
$user_providers = $this->get_available_providers_for_user( get_userdata( $user_id ) );
if ( ! $user_providers ) {
return null;
}
if ( 1 === count( $user_providers ) ) {
$provider = key( $user_providers );
} else {
$provider = get_user_meta( $user_id, $this->_provider_user_meta_key, true );
// If the provider specified isn't enabled, just grab the first one that is.
if ( ! $provider || ! isset( $user_providers[ $provider ] ) ) {
$provider = key( $user_providers );
}
}
/**
* Filter the two-factor authentication provider used for this user.
*
* @param string $provider The provider currently being used.
* @param int $user_id The user ID.
*/
$provider = apply_filters( 'two_factor_primary_provider_for_user', $provider, $user_id );
return $this->helper->get_provider_instance( $provider );
}
/**
* Quick boolean check for whether a given user is using two-step.
*
* @param int $user_id Optional. User ID. Default is 'null'.
*
* @return bool|null True if they are using it. False if not using it. Null if disabled site-wide.
*/
public function is_user_using_two_factor( $user_id = null ) {
if ( defined( 'ITSEC_DISABLE_TWO_FACTOR' ) && ITSEC_DISABLE_TWO_FACTOR ) {
return null;
}
return (bool) $this->get_primary_provider_for_user( $user_id );
}
/**
* Determine if a Sync Two-Factor override is active.
*
* @param int $user_id User ID.
*
* @return bool True if the override is active. False otherwise.
*/
public function is_sync_override_active( $user_id ) {
$sync_override = (int) get_user_option( 'itsec_two_factor_override', $user_id );
if ( 1 !== $sync_override ) {
return false;
}
$override_expires = (int) get_user_option( 'itsec_two_factor_override_expires', $user_id );
if ( current_time( 'timestamp' ) > $override_expires ) {
return false;
}
$post_data = $_POST;
ITSEC_Log::add_debug( 'two_factor', "sync_override::$user_id", compact( 'user_id', 'sync_override', 'override_expires', 'post_data' ), compact( 'user_id' ) );
return true;
}
/**
* Register the 2fa interstitial.
*
* @param ITSEC_Lib_Login_Interstitial $lib
*/
public function register_interstitial( $lib ) {
require_once( dirname( __FILE__ ) . '/class-itsec-two-factor-interstitial.php' );
require_once( dirname( __FILE__ ) . '/class-itsec-two-factor-on-board.php' );
$interstitial = new ITSEC_Two_Factor_Interstitial( $this );
$interstitial->run();
$lib->register( '2fa', $interstitial );
$lib->register( '2fa-on-board', new ITSEC_Two_Factor_On_Board( $this ) );
}
/**
* Enqueue the css/profile-page.css file.
*/
public function add_profile_page_styling() {
wp_enqueue_style( 'itsec-two-factor-profile-page', plugins_url( 'css/profile-page.css', __FILE__ ), array(), ITSEC_Core::get_plugin_build() );
$this->load_helper();
$this->helper->get_enabled_provider_instances();
}
/**
* Register the Two Factor Email method notification.
*
* @param array $notifications
*
* @return array
*/
public function register_notifications( $notifications ) {
$notifications['two-factor-email'] = array(
'slug' => 'two-factor-email',
'schedule' => ITSEC_Notification_Center::S_NONE,
'recipient' => ITSEC_Notification_Center::R_USER,
'subject_editable' => true,
'message_editable' => true,
'tags' => array( 'username', 'display_name', 'site_title' ),
'module' => 'two-factor',
);
$notifications['two-factor-confirm-email'] = array(
'slug' => 'two-factor-confirm-email',
'schedule' => ITSEC_Notification_Center::S_NONE,
'recipient' => ITSEC_Notification_Center::R_USER,
'subject_editable' => true,
'message_editable' => true,
'tags' => array( 'username', 'display_name', 'site_title' ),
'module' => 'two-factor',
'optional' => true,
);
return $notifications;
}
/**
* Provide translated strings for the Two Factor Email method notification.
*
* @return array
*/
public function two_factor_email_method_strings() {
/* translators: Do not translate the curly brackets or their contents, those are placeholders. */
$message = __( 'Hi {{ $display_name }},
Click the button to continue or manually enter the authentication code below to finish logging in.', 'it-l10n-ithemes-security-pro' );
return array(
'label' => __( 'Two-Factor Email', 'it-l10n-ithemes-security-pro' ),
'description' => sprintf(
__( 'The %1$sTwo-Factor Authentication%2$s module sends an email containing the Authentication Code for users using email as their two-factor provider.', 'it-l10n-ithemes-security-pro' ),
ITSEC_Core::get_link_for_settings_route( ITSEC_Core::get_settings_module_route( 'two-factor' ) ),
''
),
'subject' => __( 'Login Authentication Code', 'it-l10n-ithemes-security-pro' ),
'message' => $message,
'tags' => array(
'username' => __( "The recipient’s WordPress username.", 'it-l10n-ithemes-security-pro' ),
'display_name' => __( "The recipient’s WordPress display name.", 'it-l10n-ithemes-security-pro' ),
'site_title' => __( 'The WordPress Site Title. Can be changed under Settings → General → Site Title', 'it-l10n-ithemes-security-pro' ),
)
);
}
/**
* Provide translated strings for the Two Factor Confirm Email method notification.
*
* @return array
*/
public function two_factor_confirm_email_method_strings() {
/* translators: Do not translate the curly brackets or their contents, those are placeholders. */
$message = __( 'Hi {{ $display_name }},
Click the button to continue or manually enter the authentication code below to finish setting up Two-Factor.', 'it-l10n-ithemes-security-pro' );
$desc = sprintf(
__( 'The %1$sTwo-Factor Authentication%2$s module sends an email containing the Authentication Code for users when they are setting up Two-Factor. Try to keep the email similar to the Two Factor Email.', 'it-l10n-ithemes-security-pro' ),
ITSEC_Core::get_link_for_settings_route( ITSEC_Core::get_settings_module_route( 'two-factor' ) ),
''
);
$desc .= ' ' . __( 'Disabling this email will disable the Two-Factor Email Confirmation flow.', 'it-l10n-ithemes-security-pro' );
return array(
'label' => __( 'Two-Factor Email Confirmation', 'it-l10n-ithemes-security-pro' ),
'description' => $desc,
'subject' => __( 'Login Authentication Code', 'it-l10n-ithemes-security-pro' ),
'message' => $message,
'tags' => array(
'username' => __( 'The recipient’s WordPress username.', 'it-l10n-ithemes-security-pro' ),
'display_name' => __( 'The recipient’s WordPress display name.', 'it-l10n-ithemes-security-pro' ),
'site_title' => __( 'The WordPress Site Title. Can be changed under Settings → General → Site Title', 'it-l10n-ithemes-security-pro' ),
)
);
}
public function get_helper() {
return $this->helper;
}
//
// Deprecated
//
/**
* @deprecated 7.0.0
*/
const REMEMBER_COOKIE = 'itsec_remember_2fa';
/**
* @deprecated 7.0.0
*/
const REMEMBER_META_KEY = '_itsec_remember_2fa';
/**
* Set the remember 2fa cookie.
*
* @param WP_User $user
*
* @return bool
* @deprecated 7.0.0
*
*/
public function set_remember_cookie( $user ) {
$c = ITSEC_Modules::get_container();
$api = \iThemesSecurity\Pro_Two_Factor\API::class;
_deprecated_function(
__METHOD__,
'7.0.0',
$api . '::set_remember_cookie'
);
if ( $c->has( $api ) ) {
return $c->get( $api )->set_remember_cookie( $user );
}
return false;
}
/**
* Clear the remember 2fa cookie.
*
* @return bool
* @deprecated 7.0.0
*
*/
public function clear_remember_cookie() {
$c = ITSEC_Modules::get_container();
$api = \iThemesSecurity\Pro_Two_Factor\API::class;
_deprecated_function(
__METHOD__,
'7.0.0',
$api . '::clear_remember_cookie'
);
if ( $c->has( $api ) ) {
return $c->get( $api )->clear_remember_cookie();
}
return false;
}
/**
* Is the user allowed to remember 2fa.
*
* @param WP_User $user
*
* @return bool
* @deprecated 7.0.0
*
*/
public function is_remember_allowed( $user ) {
$c = ITSEC_Modules::get_container();
$api = \iThemesSecurity\Pro_Two_Factor\API::class;
_deprecated_function(
__METHOD__,
'7.0.0',
$api . '::is_remember_allowed'
);
if ( $c->has( $api ) ) {
return $c->get( $api )->is_remember_allowed( $user );
}
return false;
}
}