diff --git a/wp-content/plugins/two-factor/assets/screenshot-1.png b/wp-content/plugins/two-factor/assets/screenshot-1.png index 545b45ee..001fb2ae 100644 Binary files a/wp-content/plugins/two-factor/assets/screenshot-1.png and b/wp-content/plugins/two-factor/assets/screenshot-1.png differ diff --git a/wp-content/plugins/two-factor/assets/screenshot-2.png b/wp-content/plugins/two-factor/assets/screenshot-2.png index b9835800..9fb4f742 100644 Binary files a/wp-content/plugins/two-factor/assets/screenshot-2.png and b/wp-content/plugins/two-factor/assets/screenshot-2.png differ diff --git a/wp-content/plugins/two-factor/assets/screenshot-3.png b/wp-content/plugins/two-factor/assets/screenshot-3.png index a9810f87..b866bbb0 100644 Binary files a/wp-content/plugins/two-factor/assets/screenshot-3.png and b/wp-content/plugins/two-factor/assets/screenshot-3.png differ diff --git a/wp-content/plugins/two-factor/class-two-factor-compat.php b/wp-content/plugins/two-factor/class-two-factor-compat.php index 731a2dff..d7b4f46a 100644 --- a/wp-content/plugins/two-factor/class-two-factor-compat.php +++ b/wp-content/plugins/two-factor/class-two-factor-compat.php @@ -50,6 +50,6 @@ class Two_Factor_Compat { * @return boolean */ public function jetpack_is_sso_active() { - return ( method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'sso' ) ); + return ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'sso' ) ); } } diff --git a/wp-content/plugins/two-factor/class-two-factor-core.php b/wp-content/plugins/two-factor/class-two-factor-core.php index 3c0df20a..5eaa765a 100644 --- a/wp-content/plugins/two-factor/class-two-factor-core.php +++ b/wp-content/plugins/two-factor/class-two-factor-core.php @@ -57,7 +57,7 @@ class Two_Factor_Core { const USER_PASSWORD_WAS_RESET_KEY = '_two_factor_password_was_reset'; /** - * URL query paramater used for our custom actions. + * URL query parameter used for our custom actions. * * @var string */ @@ -93,8 +93,7 @@ class Two_Factor_Core { * @since 0.1-dev */ public static function add_hooks( $compat ) { - add_action( 'plugins_loaded', array( __CLASS__, 'load_textdomain' ) ); - add_action( 'init', array( __CLASS__, 'get_providers' ) ); + add_action( 'init', array( __CLASS__, 'get_providers' ) ); // @phpstan-ignore return.void add_action( 'wp_login', array( __CLASS__, 'wp_login' ), 10, 2 ); add_filter( 'wp_login_errors', array( __CLASS__, 'maybe_show_reset_password_notice' ) ); add_action( 'after_password_reset', array( __CLASS__, 'clear_password_reset_notice' ) ); @@ -133,29 +132,135 @@ class Two_Factor_Core { } /** - * Loads the plugin's text domain. + * Delete all plugin data on uninstall. * - * Sites on WordPress 4.6+ benefit from just-in-time loading of translations. + * @return void */ - public static function load_textdomain() { - load_plugin_textdomain( 'two-factor' ); + public static function uninstall() { + // Keep this updated as user meta keys are added or removed. + $user_meta_keys = array( + self::PROVIDER_USER_META_KEY, + self::ENABLED_PROVIDERS_USER_META_KEY, + self::USER_META_NONCE_KEY, + self::USER_RATE_LIMIT_KEY, + self::USER_FAILED_LOGIN_ATTEMPTS_KEY, + self::USER_PASSWORD_WAS_RESET_KEY, + ); + + $option_keys = array(); + + $providers = self::get_default_providers(); + + /** This filter is documented in the get_providers() method */ + $additional_providers = apply_filters( 'two_factor_providers', $providers ); + + // Merge them with the default providers. + if ( ! empty( $additional_providers ) ) { + $providers = array_merge( $providers, $additional_providers ); + } + + foreach ( self::get_providers_classes( $providers ) as $provider_class ) { + // Merge with provider-specific user meta keys. + if ( method_exists( $provider_class, 'uninstall_user_meta_keys' ) ) { + try { + $user_meta_keys = array_merge( + $user_meta_keys, + call_user_func( array( $provider_class, 'uninstall_user_meta_keys' ) ) + ); + } catch ( Exception $e ) { + // Do nothing. + } + } + + // Merge with provider-specific option keys. + if ( method_exists( $provider_class, 'uninstall_options' ) ) { + try { + $option_keys = array_merge( + $option_keys, + call_user_func( array( $provider_class, 'uninstall_options' ) ) + ); + } catch ( Exception $e ) { + // Do nothing. + } + } + } + + // Delete options first since that is faster. + if ( ! empty( $option_keys ) ) { + foreach ( $option_keys as $option_key ) { + delete_option( $option_key ); + } + } + + foreach ( $user_meta_keys as $meta_key ) { + delete_metadata( 'user', null, $meta_key, null, true ); + } } /** - * For each provider, include it and then instantiate it. + * Get the registered providers of which some might not be enabled. * - * @since 0.1-dev - * - * @return array + * @return array List of provider keys and paths to class files. */ - public static function get_providers() { - $providers = array( + private static function get_default_providers() { + return array( 'Two_Factor_Email' => TWO_FACTOR_DIR . 'providers/class-two-factor-email.php', 'Two_Factor_Totp' => TWO_FACTOR_DIR . 'providers/class-two-factor-totp.php', 'Two_Factor_FIDO_U2F' => TWO_FACTOR_DIR . 'providers/class-two-factor-fido-u2f.php', 'Two_Factor_Backup_Codes' => TWO_FACTOR_DIR . 'providers/class-two-factor-backup-codes.php', 'Two_Factor_Dummy' => TWO_FACTOR_DIR . 'providers/class-two-factor-dummy.php', ); + } + + /** + * Get the classnames for specific providers. + * + * @param array $providers List of paths to provider class files indexed by class names. + * + * @return array List of provider keys and classnames. + */ + private static function get_providers_classes( $providers ) { + foreach ( $providers as $provider_key => $path ) { + if ( ! empty( $path ) && is_readable( $path ) ) { + require_once $path; + } + + $class = $provider_key; + + /** + * Filters the classname for a provider. The dynamic portion of the filter is the defined providers key. + * + * @param string $class The PHP Classname of the provider. + * @param string $path The provided provider path to be included. + */ + $class = apply_filters( "two_factor_provider_classname_{$provider_key}", $class, $path ); + + /** + * Confirm that it's been successfully included. + */ + if ( class_exists( $class ) ) { + $providers[ $provider_key ] = $class; + } else { + unset( $providers[ $provider_key ] ); + } + } + + return $providers; + } + + /** + * Get all registered two-factor providers with keys as the original + * provider class names and the values as the provider class instances. + * + * @see Two_Factor_Core::get_enabled_providers_for_user() + * @see Two_Factor_Core::get_supported_providers_for_user() + * + * @since 0.1-dev + * + * @return array + */ + public static function get_providers() { + $providers = self::get_default_providers(); /** * Filter the supplied providers. @@ -180,37 +285,43 @@ class Two_Factor_Core { ); } - /** - * For each filtered provider, - */ - foreach ( $providers as $provider_key => $path ) { - require_once $path; + // Map provider keys to classes so that we can instantiate them. + $providers = self::get_providers_classes( $providers ); - $class = $provider_key; - - /** - * Filters the classname for a provider. The dynamic portion of the filter is the defined providers key. - * - * @param string $class The PHP Classname of the provider. - * @param string $path The provided provider path to be included. - */ - $class = apply_filters( "two_factor_provider_classname_{$provider_key}", $class, $path ); - - /** - * Confirm that it's been successfully included before instantiating. - */ - if ( class_exists( $class ) ) { - try { - $providers[ $provider_key ] = call_user_func( array( $class, 'get_instance' ) ); - } catch ( Exception $e ) { - unset( $providers[ $provider_key ] ); - } + // TODO: Refactor this to avoid instantiating the provider instances every time this method is called. + foreach ( $providers as $provider_key => $provider_class ) { + try { + $providers[ $provider_key ] = call_user_func( array( $provider_class, 'get_instance' ) ); + } catch ( Exception $e ) { + unset( $providers[ $provider_key ] ); } } return $providers; } + /** + * Get providers available for user which may not be enabled or configured. + * + * @see Two_Factor_Core::get_enabled_providers_for_user() + * @see Two_Factor_Core::get_available_providers_for_user() + * + * @param WP_User|int|null $user User ID. + * @return array List of provider instances indexed by provider key. + */ + public static function get_supported_providers_for_user( $user = null ) { + $user = self::fetch_user( $user ); + $providers = self::get_providers(); + + /** + * List of providers available to user which may not be enabled or configured. + * + * @param array $providers List of available provider instances indexed by provider key. + * @param int|WP_User $user User ID. + */ + return apply_filters( 'two_factor_providers_for_user', $providers, $user ); + } + /** * Enable the dummy method only during debugging. * @@ -397,9 +508,13 @@ class Two_Factor_Core { } /** - * Get all Two-Factor Auth providers that are enabled for the specified|current user. + * Get two-factor providers that are enabled for the specified (or current) user + * but might not be configured, yet. * - * @param int|WP_User $user Optonal. User ID, or WP_User object of the the user. Defaults to current user. + * @see Two_Factor_Core::get_supported_providers_for_user() + * @see Two_Factor_Core::get_available_providers_for_user() + * + * @param int|WP_User $user Optional. User ID, or WP_User object of the the user. Defaults to current user. * @return array */ public static function get_enabled_providers_for_user( $user = null ) { @@ -408,7 +523,7 @@ class Two_Factor_Core { return array(); } - $providers = self::get_providers(); + $providers = self::get_supported_providers_for_user( $user ); $enabled_providers = get_user_meta( $user->ID, self::ENABLED_PROVIDERS_USER_META_KEY, true ); if ( empty( $enabled_providers ) ) { $enabled_providers = array(); @@ -425,10 +540,14 @@ class Two_Factor_Core { } /** - * Get all Two-Factor Auth providers that are both enabled and configured for the specified|current user. + * Get all two-factor providers that are both enabled and configured + * for the specified (or current) user. * - * @param int|WP_User $user Optonal. User ID, or WP_User object of the the user. Defaults to current user. - * @return array + * @see Two_Factor_Core::get_supported_providers_for_user() + * @see Two_Factor_Core::get_enabled_providers_for_user() + * + * @param int|WP_User $user Optional. User ID, or WP_User object of the the user. Defaults to current user. + * @return array List of provider instances. */ public static function get_available_providers_for_user( $user = null ) { $user = self::fetch_user( $user ); @@ -436,8 +555,8 @@ class Two_Factor_Core { return array(); } - $providers = self::get_providers(); - $enabled_providers = self::get_enabled_providers_for_user( $user ); + $providers = self::get_supported_providers_for_user( $user ); // Returns full objects. + $enabled_providers = self::get_enabled_providers_for_user( $user ); // Returns just the keys. $configured_providers = array(); foreach ( $providers as $provider_key => $provider ) { @@ -452,7 +571,7 @@ class Two_Factor_Core { /** * Fetch the provider for the request based on the user preferences. * - * @param int|WP_User $user Optonal. User ID, or WP_User object of the the user. Defaults to current user. + * @param int|WP_User $user Optional. User ID, or WP_User object of the the user. Defaults to current user. * @param null|string|object $preferred_provider Optional. The name of the provider, the provider, or empty. * @return null|object The provider */ @@ -470,7 +589,7 @@ class Two_Factor_Core { // Default to the currently logged in provider. if ( ! $preferred_provider && get_current_user_id() === $user->ID ) { $session = self::get_current_user_session(); - if ( ! empty( $session['two-factor-provider'] ) ) { + if ( ! empty( $session['two-factor-provider'] ) ) { $preferred_provider = $session['two-factor-provider']; } } @@ -485,12 +604,31 @@ class Two_Factor_Core { return self::get_primary_provider_for_user( $user ); } + /** + * Get the name of the primary provider selected by the user + * and enabled for the user. + * + * @param WP_User|int $user User ID or instance. + * + * @return string|null + */ + private static function get_primary_provider_key_selected_for_user( $user ) { + $primary_provider = get_user_meta( $user->ID, self::PROVIDER_USER_META_KEY, true ); + $available_providers = self::get_available_providers_for_user( $user ); + + if ( ! empty( $primary_provider ) && ! empty( $available_providers[ $primary_provider ] ) ) { + return $primary_provider; + } + + return null; + } + /** * Gets the Two-Factor Auth provider for the specified|current user. * * @since 0.1-dev * - * @param int|WP_User $user Optonal. User ID, or WP_User object of the the user. Defaults to current user. + * @param int|WP_User $user Optional. User ID, or WP_User object of the the user. Defaults to current user. * @return object|null */ public static function get_primary_provider_for_user( $user = null ) { @@ -499,7 +637,7 @@ class Two_Factor_Core { return null; } - $providers = self::get_providers(); + $providers = self::get_supported_providers_for_user( $user ); $available_providers = self::get_available_providers_for_user( $user ); // If there's only one available provider, force that to be the primary. @@ -508,7 +646,7 @@ class Two_Factor_Core { } elseif ( 1 === count( $available_providers ) ) { $provider = key( $available_providers ); } else { - $provider = get_user_meta( $user->ID, self::PROVIDER_USER_META_KEY, true ); + $provider = self::get_primary_provider_key_selected_for_user( $user ); // If the provider specified isn't enabled, just grab the first one that is. if ( ! isset( $available_providers[ $provider ] ) ) { @@ -536,7 +674,7 @@ class Two_Factor_Core { * * @since 0.1-dev * - * @param int|WP_User $user Optonal. User ID, or WP_User object of the the user. Defaults to current user. + * @param int|WP_User $user Optional. User ID, or WP_User object of the the user. Defaults to current user. * @return bool */ public static function is_user_using_two_factor( $user = null ) { @@ -693,8 +831,8 @@ class Two_Factor_Core { echo '
'; printf( _n( - 'WARNING: Your account has attempted to login without providing a valid two factor token. The last failed login occured %2$s ago. If this wasn\'t you, you should reset your password.', - 'WARNING: Your account has attempted to login %1$s times without providing a valid two factor token. The last failed login occured %2$s ago. If this wasn\'t you, you should reset your password.', + 'WARNING: Your account has attempted to login without providing a valid two factor token. The last failed login occurred %2$s ago. If this wasn\'t you, you should reset your password.', + 'WARNING: Your account has attempted to login %1$s times without providing a valid two factor token. The last failed login occurred %2$s ago. If this wasn\'t you, you should reset your password.', $failed_login_count, 'two-factor' ), @@ -1171,6 +1309,7 @@ class Two_Factor_Core { * Return a falsey value (false, 0) if you wish to never require revalidation. * * @param int $two_factor_revalidate_time The grace time between last validation time and when it'll be accepted. Default 10 minutes (in seconds). + * @param int $user_id The user ID. * @param string $context The context in use, 'display' or 'save'. Save has twice the grace time. */ $two_factor_revalidate_time = apply_filters( 'two_factor_revalidate_time', 10 * MINUTE_IN_SECONDS, $user_id, $context ); @@ -1409,10 +1548,14 @@ class Two_Factor_Core { } // Update the session metadata with the revalidation details. - self::update_current_user_session( array( - 'two-factor-provider' => $provider->get_key(), - 'two-factor-login' => time(), - ) ); + self::update_current_user_session( + array( + 'two-factor-provider' => $provider->get_key(), + 'two-factor-login' => time(), + ) + ); + + do_action( 'two_factor_user_revalidated', $user, $provider ); // Must be global because that's how login_header() uses it. global $interim_login; @@ -1444,7 +1587,7 @@ class Two_Factor_Core { * @param object $provider The Two Factor Provider. * @param WP_User $user The user being authenticated. * @param bool $is_post_request Whether the request is a POST request. - * @return false|WP_Error|true WP_Error when an error occurs, true when the user is authenticated, false if no action occured. + * @return false|WP_Error|true WP_Error when an error occurs, true when the user is authenticated, false if no action occurred. */ public static function process_provider( $provider, $user, $is_post_request ) { if ( ! $provider ) { @@ -1480,7 +1623,7 @@ class Two_Factor_Core { // Ask the provider to verify the second factor. if ( true !== $provider->validate_authentication( $user ) ) { - // Store the last time a failed login occured. + // Store the last time a failed login occurred. update_user_meta( $user->ID, self::USER_RATE_LIMIT_KEY, time() ); // Store the number of failed login attempts. @@ -1646,7 +1789,7 @@ class Two_Factor_Core { ) ); - login_header( __( 'Password Reset', 'two-factor' ), '', $error ); + login_header( __( 'Password Reset', 'two-factor' ), '', $error ); login_footer(); } @@ -1694,95 +1837,130 @@ class Two_Factor_Core { * @param WP_User $user WP_User object of the logged-in user. */ public static function user_two_factor_options( $user ) { + $notices = []; + + $providers = self::get_supported_providers_for_user( $user ); + wp_enqueue_style( 'user-edit-2fa', plugins_url( 'user-edit.css', __FILE__ ), array(), TWO_FACTOR_VERSION ); $enabled_providers = array_keys( self::get_available_providers_for_user( $user ) ); - $primary_provider = self::get_primary_provider_for_user( $user->ID ); - - if ( ! empty( $primary_provider ) && is_object( $primary_provider ) ) { - $primary_provider_key = $primary_provider->get_key(); - } else { - $primary_provider_key = null; - } // This is specific to the current session, not the displayed user. $show_2fa_options = self::current_user_can_update_two_factor_options(); if ( ! $show_2fa_options ) { - $url = self::get_user_two_factor_revalidate_url(); - $url = add_query_arg( 'redirect_to', urlencode( self::get_user_settings_page_url( $user->ID ) . '#two-factor-options' ), $url ); + $url = add_query_arg( + 'redirect_to', + urlencode( self::get_user_settings_page_url( $user->ID ) . '#two-factor-options' ), + self::get_user_two_factor_revalidate_url() + ); - printf( - '

%s

', - sprintf( - __( 'To update your Two-Factor options, you must first revalidate your session.', 'two-factor' ) . - '
' . __( 'Revalidate now', 'two-factor' ) . '', - esc_url( $url ) - ) + $notices['warning two-factor-warning-revalidate-session'] = sprintf( + esc_html__( 'To update your Two-Factor options, you must first revalidate your session.', 'two-factor' ) . + ' ' . esc_html__( 'Revalidate now', 'two-factor' ) . '', + esc_url( $url ) ); } - printf( - '
', - $show_2fa_options ? '' : 'disabled="disabled"' - ); + if ( empty( $providers ) ) { + $notices['notice two-factor-notice-no-providers-supported'] = esc_html__( 'No providers are available for your account.', 'two-factor' ); + } - wp_nonce_field( 'user_two_factor_options', '_nonce_user_two_factor_options', false ); + // Suggest enabling a backup method if only one method is enabled and there are more available. + if ( count( $providers ) > 1 && 1 === count( $enabled_providers ) ) { + $notices['warning two-factor-warning-suggest-backup'] = esc_html__( 'To prevent being locked out of your account, consider enabling a backup method like Recovery Codes in case you lose access to your primary authentication method.', 'two-factor' ); + } ?> - - - - - - -
- - - - - - - - - - - - $object ) : ?> - - - - - - - -
/> /> - - -
-
-
- + $notice ) : ?> +
+

+
+ + +
> + +
+ + +

+ +

+ + + + + + + $object ) : ?> + + + + + + + +
+ + + + + + + + + get_key() === $provider_to_delete ) { + delete_user_meta( $user_id, self::PROVIDER_USER_META_KEY ); + } + + return (bool) update_user_meta( $user_id, self::ENABLED_PROVIDERS_USER_META_KEY, $enabled_providers ); } /** @@ -1877,7 +2050,7 @@ class Two_Factor_Core { return; } - $providers = self::get_providers(); + $providers = self::get_supported_providers_for_user( $user_id ); $enabled_providers = $_POST[ self::ENABLED_PROVIDERS_USER_META_KEY ]; $existing_providers = self::get_enabled_providers_for_user( $user_id ); @@ -1889,6 +2062,8 @@ class Two_Factor_Core { $new_provider = isset( $_POST[ self::PROVIDER_USER_META_KEY ] ) ? $_POST[ self::PROVIDER_USER_META_KEY ] : ''; if ( ! empty( $new_provider ) && in_array( $new_provider, $enabled_providers, true ) ) { update_user_meta( $user_id, self::PROVIDER_USER_META_KEY, $new_provider ); + } else { + delete_user_meta( $user_id, self::PROVIDER_USER_META_KEY ); } // Have we changed the two-factor settings for the current user? Alter their session metadata. diff --git a/wp-content/plugins/two-factor/includes/Yubico/U2F.php b/wp-content/plugins/two-factor/includes/Yubico/U2F.php index e819bbc1..bbb6e9a0 100644 --- a/wp-content/plugins/two-factor/includes/Yubico/U2F.php +++ b/wp-content/plugins/two-factor/includes/Yubico/U2F.php @@ -501,7 +501,7 @@ class Error extends \Exception * @param int $code * @param \Exception|null $previous */ - public function __construct($message, $code, \Exception $previous = null) { + public function __construct($message, $code, ?\Exception $previous = null) { parent::__construct($message, $code, $previous); } } diff --git a/wp-content/plugins/two-factor/providers/class-two-factor-backup-codes.php b/wp-content/plugins/two-factor/providers/class-two-factor-backup-codes.php index c53a8448..2a3345b4 100644 --- a/wp-content/plugins/two-factor/providers/class-two-factor-backup-codes.php +++ b/wp-content/plugins/two-factor/providers/class-two-factor-backup-codes.php @@ -40,7 +40,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider { add_action( 'two_factor_user_options_' . __CLASS__, array( $this, 'user_options' ) ); add_action( 'admin_notices', array( $this, 'admin_notices' ) ); - return parent::__construct(); + parent::__construct(); } /** @@ -214,6 +214,25 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider { ID, self::BACKUP_CODES_META_KEY, true ); } + $code_length = $this->get_backup_code_length( $user ); + for ( $i = 0; $i < $num_codes; $i++ ) { - $code = $this->get_code(); + $code = $this->get_code( $code_length ); $codes_hashed[] = wp_hash_password( $code ); $codes[] = $code; unset( $code ); @@ -326,11 +347,15 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider { */ public function authentication_page( $user ) { require_once ABSPATH . '/wp-admin/includes/template.php'; + + $code_length = $this->get_backup_code_length( $user ); + $code_placeholder = str_repeat( 'X', $code_length ); + ?> -


+

- +

ID, self::BACKUP_CODES_META_KEY, $backup_codes ); } + + /** + * Return user meta keys to delete during plugin uninstall. + * + * @return array + */ + public static function uninstall_user_meta_keys() { + return array( + self::BACKUP_CODES_META_KEY, + ); + } } diff --git a/wp-content/plugins/two-factor/providers/class-two-factor-dummy.php b/wp-content/plugins/two-factor/providers/class-two-factor-dummy.php index 34c6b57d..cc9a6119 100644 --- a/wp-content/plugins/two-factor/providers/class-two-factor-dummy.php +++ b/wp-content/plugins/two-factor/providers/class-two-factor-dummy.php @@ -21,7 +21,7 @@ class Two_Factor_Dummy extends Two_Factor_Provider { */ protected function __construct() { add_action( 'two_factor_user_options_' . __CLASS__, array( $this, 'user_options' ) ); - return parent::__construct(); + parent::__construct(); } /** diff --git a/wp-content/plugins/two-factor/providers/class-two-factor-email.php b/wp-content/plugins/two-factor/providers/class-two-factor-email.php index c6f5881f..038e35cb 100644 --- a/wp-content/plugins/two-factor/providers/class-two-factor-email.php +++ b/wp-content/plugins/two-factor/providers/class-two-factor-email.php @@ -42,7 +42,7 @@ class Two_Factor_Email extends Two_Factor_Provider { */ protected function __construct() { add_action( 'two_factor_user_options_' . __CLASS__, array( $this, 'user_options' ) ); - return parent::__construct(); + parent::__construct(); } /** @@ -63,6 +63,22 @@ class Two_Factor_Email extends Two_Factor_Provider { return __( 'Send a code to your email', 'two-factor' ); } + /** + * Get the email token length. + * + * @return int Email token string length. + */ + private function get_token_length() { + /** + * Number of characters in the email token. + * + * @param int $token_length Number of characters in the email token. + */ + $token_length = (int) apply_filters( 'two_factor_email_token_length', 8 ); + + return $token_length; + } + /** * Generate the user token. * @@ -72,7 +88,7 @@ class Two_Factor_Email extends Two_Factor_Provider { * @return string */ public function generate_token( $user_id ) { - $token = $this->get_code(); + $token = $this->get_code( $this->get_token_length() ); update_user_meta( $user_id, self::TOKEN_META_KEY_TIMESTAMP, time() ); update_user_meta( $user_id, self::TOKEN_META_KEY, wp_hash( $token ) ); @@ -146,10 +162,21 @@ class Two_Factor_Email extends Two_Factor_Provider { * Number of seconds the token is considered valid * after the generation. * + * @deprecated 0.11.0 Use {@see 'two_factor_email_token_ttl'} instead. + * * @param integer $token_ttl Token time-to-live in seconds. * @param integer $user_id User ID. */ - return (int) apply_filters( 'two_factor_token_ttl', $token_ttl, $user_id ); + $token_ttl = (int) apply_filters_deprecated( 'two_factor_token_ttl', array( $token_ttl, $user_id ), '0.11.0', 'two_factor_email_token_ttl' ); + + /** + * Number of seconds the token is considered valid + * after the generation. + * + * @param integer $token_ttl Token time-to-live in seconds. + * @param integer $user_id User ID. + */ + return (int) apply_filters( 'two_factor_email_token_ttl', $token_ttl, $user_id ); } /** @@ -259,12 +286,15 @@ class Two_Factor_Email extends Two_Factor_Provider { $this->generate_and_email_token( $user ); } + $token_length = $this->get_token_length(); + $token_placeholder = str_repeat( 'X', $token_length ); + require_once ABSPATH . '/wp-admin/includes/template.php'; ?>

- +

@@ -287,7 +317,7 @@ class Two_Factor_Email extends Two_Factor_Provider { * Send the email code if missing or requested. Stop the authentication * validation if a new token has been generated and sent. * - * @param WP_USer $user WP_User object of the logged-in user. + * @param WP_User $user WP_User object of the logged-in user. * @return boolean */ public function pre_process_authentication( $user ) { @@ -351,4 +381,16 @@ class Two_Factor_Email extends Two_Factor_Provider {

ID}", '_nonce_user_security_keys' ); $new_key = false; @@ -230,7 +234,7 @@ class Two_Factor_FIDO_U2F_Admin { * @static * * @param int $user_id User ID. - * @return false + * @return void|never */ public static function catch_submission( $user_id ) { if ( ! empty( $_REQUEST['do_new_security_key'] ) ) { @@ -243,7 +247,7 @@ class Two_Factor_FIDO_U2F_Admin { Two_Factor_FIDO_U2F::add_security_key( $user_id, $reg ); } catch ( Exception $e ) { - return false; + return; } delete_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY ); diff --git a/wp-content/plugins/two-factor/providers/class-two-factor-fido-u2f.php b/wp-content/plugins/two-factor/providers/class-two-factor-fido-u2f.php index 52d28f57..cd569997 100644 --- a/wp-content/plugins/two-factor/providers/class-two-factor-fido-u2f.php +++ b/wp-content/plugins/two-factor/providers/class-two-factor-fido-u2f.php @@ -65,7 +65,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider { add_action( 'two_factor_user_options_' . __CLASS__, array( $this, 'user_options' ) ); - return parent::__construct(); + parent::__construct(); } /** @@ -143,7 +143,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider { * @since 0.1-dev * * @param WP_User $user WP_User object of the logged-in user. - * @return null + * @return void */ public function authentication_page( $user ) { require_once ABSPATH . '/wp-admin/includes/template.php'; @@ -165,7 +165,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider { ?>

delete_user_totp_key( $user_id ); + if ( ! Two_Factor_Core::disable_provider_for_user( $user_id, 'Two_Factor_Totp' ) ) { + return new WP_Error( 'db_error', __( 'Unable to disable TOTP provider for this user.', 'two-factor' ), array( 'status' => 500 ) ); + } + ob_start(); $this->user_two_factor_options( $user ); $html = ob_get_clean(); @@ -164,7 +168,7 @@ class Two_Factor_Totp extends Two_Factor_Provider { /** * REST API endpoint for setting up TOTP. * - * @param WP_Rest_Request $request The Rest Request object. + * @param WP_REST_Request $request The Rest Request object. * @return WP_Error|array Array of data on success, WP_Error on error. */ public function rest_setup_totp( $request ) { @@ -203,8 +207,8 @@ class Two_Factor_Totp extends Two_Factor_Provider { /** * Generates a URL that can be used to create a QR code. * - * @param WP_User $user The user to generate a URL for. - * @param string $key The secret key. + * @param WP_User $user The user to generate a URL for. + * @param string $secret_key The secret key. * * @return string */ @@ -260,13 +264,13 @@ class Two_Factor_Totp extends Two_Factor_Provider { * Display TOTP options on the user settings page. * * @param WP_User $user The current user being edited. - * @return false + * @return void * * @codeCoverageIgnore */ public function user_two_factor_options( $user ) { if ( ! isset( $user->ID ) ) { - return false; + return; } $key = $this->get_user_totp_key( $user->ID ); @@ -318,6 +322,15 @@ class Two_Factor_Totp extends Two_Factor_Provider { qr.make(); document.querySelector( '#two-factor-qr-code a' ).innerHTML = qr.createSvgTag( 5 ); + + // For accessibility, markup the SVG with a title and role. + var svg = document.querySelector( '#two-factor-qr-code a svg' ), + title = document.createElement( 'title' ); + + svg.role = 'image'; + svg.ariaLabel = ; + title.innerText = svg.ariaLabel; + svg.appendChild( title ); }; // Run now if the document is loaded, otherwise on DOMContentLoaded. @@ -340,13 +353,20 @@ class Two_Factor_Totp extends Two_Factor_Provider { /* translators: Example auth code. */ $placeholder = sprintf( __( 'eg. %s', 'two-factor' ), '123456' ); ?> - +