updated plugin Two Factor
version 0.13.0
This commit is contained in:
parent
c950632407
commit
a212704ec2
Binary file not shown.
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 152 KiB |
Binary file not shown.
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 147 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 41 KiB |
@ -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' ) );
|
||||
}
|
||||
}
|
||||
|
@ -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 ) ) {
|
||||
// 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( $class, 'get_instance' ) );
|
||||
$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
|
||||
*/
|
||||
@ -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 '<div id="login_notice" class="message"><strong>';
|
||||
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(
|
||||
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.
|
||||
@ -1694,63 +1837,92 @@ 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(
|
||||
'<div class="notice notice-warning inline"><p>%s</p></div>',
|
||||
sprintf(
|
||||
__( 'To update your Two-Factor options, you must first revalidate your session.', 'two-factor' ) .
|
||||
'<br><a class="button" href="%s">' . __( 'Revalidate now', 'two-factor' ) . '</a>',
|
||||
$notices['warning two-factor-warning-revalidate-session'] = sprintf(
|
||||
esc_html__( 'To update your Two-Factor options, you must first revalidate your session.', 'two-factor' ) .
|
||||
' <a class="button" href="%s">' . esc_html__( 'Revalidate now', 'two-factor' ) . '</a>',
|
||||
esc_url( $url )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
printf(
|
||||
'<fieldset id="two-factor-options" %s>',
|
||||
$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' );
|
||||
}
|
||||
?>
|
||||
<h2><?php esc_html_e( 'Two-Factor Options', 'two-factor' ); ?></h2>
|
||||
|
||||
<?php foreach ( $notices as $notice_type => $notice ) : ?>
|
||||
<div class="<?php echo esc_attr( $notice_type ? 'notice inline notice-' . $notice_type : '' ); ?>">
|
||||
<p><?php echo wp_kses_post( $notice ); ?></p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<fieldset id="two-factor-options" <?php echo $show_2fa_options ? '' : 'disabled="disabled"'; ?>>
|
||||
<?php
|
||||
if ( $providers ) {
|
||||
self::render_user_providers_form( $user, $providers );
|
||||
}
|
||||
?>
|
||||
</fieldset>
|
||||
|
||||
<?php
|
||||
/**
|
||||
* Fires after the Two Factor methods table.
|
||||
*
|
||||
* To be used by Two Factor methods to add settings UI.
|
||||
*
|
||||
* @param WP_User $user The user.
|
||||
* @param array $providers List of providers available to the user.
|
||||
*
|
||||
* @since 0.1-dev
|
||||
*/
|
||||
do_action( 'show_user_security_settings', $user, $providers );
|
||||
}
|
||||
|
||||
private static function render_user_providers_form( $user, $providers ) {
|
||||
$primary_provider_key = self::get_primary_provider_key_selected_for_user( $user );
|
||||
$enabled_providers = self::get_enabled_providers_for_user( $user );
|
||||
|
||||
?>
|
||||
<p>
|
||||
<?php esc_html_e( 'Configure a primary two-factor method along with a backup method, such as Recovery Codes, to avoid being locked out if you lose access to your primary method.', 'two-factor' ); ?>
|
||||
</p>
|
||||
|
||||
<?php wp_nonce_field( 'user_two_factor_options', '_nonce_user_two_factor_options', false ); ?>
|
||||
<input type="hidden" name="<?php echo esc_attr( self::ENABLED_PROVIDERS_USER_META_KEY ); ?>[]" value="<?php /* Dummy input so $_POST value is passed when no providers are enabled. */ ?>" />
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th>
|
||||
<?php esc_html_e( 'Two-Factor Options', 'two-factor' ); ?>
|
||||
</th>
|
||||
<td>
|
||||
<table class="two-factor-methods-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-enabled" scope="col"><?php esc_html_e( 'Enabled', 'two-factor' ); ?></th>
|
||||
<th class="col-primary" scope="col"><?php esc_html_e( 'Primary', 'two-factor' ); ?></th>
|
||||
<th class="col-name" scope="col"><?php esc_html_e( 'Type', 'two-factor' ); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<table class="form-table two-factor-methods-table" role="presentation">
|
||||
<tbody>
|
||||
<?php foreach ( self::get_providers() as $provider_key => $object ) : ?>
|
||||
<?php foreach ( $providers as $provider_key => $object ) : ?>
|
||||
<tr>
|
||||
<th scope="row"><input id="enabled-<?php echo esc_attr( $provider_key ); ?>" type="checkbox" name="<?php echo esc_attr( self::ENABLED_PROVIDERS_USER_META_KEY ); ?>[]" value="<?php echo esc_attr( $provider_key ); ?>" <?php checked( in_array( $provider_key, $enabled_providers, true ) ); ?> /></th>
|
||||
<th scope="row"><input type="radio" name="<?php echo esc_attr( self::PROVIDER_USER_META_KEY ); ?>" value="<?php echo esc_attr( $provider_key ); ?>" <?php checked( $provider_key, $primary_provider_key ); ?> /></th>
|
||||
<th><?php echo esc_html( $object->get_label() ); ?></th>
|
||||
<td>
|
||||
<label class="two-factor-method-label" for="enabled-<?php echo esc_attr( $provider_key ); ?>"><?php echo esc_html( $object->get_label() ); ?></label>
|
||||
<label class="two-factor-method-label">
|
||||
<input id="enabled-<?php echo esc_attr( $provider_key ); ?>" type="checkbox" name="<?php echo esc_attr( self::ENABLED_PROVIDERS_USER_META_KEY ); ?>[]" value="<?php echo esc_attr( $provider_key ); ?>" <?php checked( in_array( $provider_key, $enabled_providers, true ) ); ?> />
|
||||
<?php echo esc_html( sprintf( __( 'Enable %s', 'two-factor' ), $object->get_label() ) ); ?>
|
||||
</label>
|
||||
<?php
|
||||
/**
|
||||
* Fires after user options are shown.
|
||||
@ -1769,20 +1941,26 @@ class Two_Factor_Core {
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<table class="form-table two-factor-primary-method-table" role="presentation">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Primary Method', 'two-factor' ); ?></th>
|
||||
<td>
|
||||
<select name="<?php echo esc_attr( self::PROVIDER_USER_META_KEY ); ?>">
|
||||
<option value=""><?php echo esc_html( __( 'Default', 'two-factor' ) ); ?></option>
|
||||
<?php foreach ( $providers as $provider_key => $object ) : ?>
|
||||
<option value="<?php echo esc_attr( $provider_key ); ?>" <?php selected( $provider_key, $primary_provider_key ); ?> <?php disabled( ! in_array( $provider_key, $enabled_providers, true ) ); ?>>
|
||||
<?php echo esc_html( $object->get_label() ); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<p class="description"><?php esc_html_e( 'Select the primary method to use for two-factor authentication when signing into this site.', 'two-factor' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Fires after the Two Factor methods table.
|
||||
*
|
||||
* To be used by Two Factor methods to add settings UI.
|
||||
*
|
||||
* @since 0.1-dev
|
||||
*/
|
||||
do_action( 'show_user_security_settings', $user );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1796,30 +1974,21 @@ class Two_Factor_Core {
|
||||
* @return bool True if the provider was enabled, false otherwise.
|
||||
*/
|
||||
public static function enable_provider_for_user( $user_id, $new_provider ) {
|
||||
$available_providers = self::get_providers();
|
||||
|
||||
if ( ! array_key_exists( $new_provider, $available_providers ) ) {
|
||||
// Ensure the provider is even available.
|
||||
if ( ! array_key_exists( $new_provider, self::get_supported_providers_for_user( $user_id ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = get_userdata( $user_id );
|
||||
$enabled_providers = self::get_enabled_providers_for_user( $user );
|
||||
$enabled_providers = self::get_enabled_providers_for_user( $user_id );
|
||||
|
||||
// Check if this is enabled already.
|
||||
if ( in_array( $new_provider, $enabled_providers ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$enabled_providers[] = $new_provider;
|
||||
$enabled = update_user_meta( $user_id, self::ENABLED_PROVIDERS_USER_META_KEY, $enabled_providers );
|
||||
|
||||
// Primary provider must be enabled.
|
||||
$has_primary = is_object( self::get_primary_provider_for_user( $user_id ) );
|
||||
|
||||
if ( ! $has_primary ) {
|
||||
$has_primary = update_user_meta( $user_id, self::PROVIDER_USER_META_KEY, $new_provider );
|
||||
}
|
||||
|
||||
return $enabled && $has_primary;
|
||||
return (bool) update_user_meta( $user_id, self::ENABLED_PROVIDERS_USER_META_KEY, $enabled_providers );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1836,23 +2005,27 @@ class Two_Factor_Core {
|
||||
* @return bool True if the provider was disabled, false otherwise.
|
||||
*/
|
||||
public static function disable_provider_for_user( $user_id, $provider_to_delete ) {
|
||||
$is_registered = array_key_exists( $provider_to_delete, self::get_providers() );
|
||||
|
||||
if ( ! $is_registered ) {
|
||||
// Check if the provider is even enabled.
|
||||
if ( ! array_key_exists( $provider_to_delete, self::get_supported_providers_for_user( $user_id ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$old_enabled_providers = self::get_enabled_providers_for_user( $user_id );
|
||||
$is_enabled = in_array( $provider_to_delete, $old_enabled_providers );
|
||||
$enabled_providers = self::get_enabled_providers_for_user( $user_id );
|
||||
|
||||
if ( ! $is_enabled ) {
|
||||
// Check if this is disabled already.
|
||||
if ( ! in_array( $provider_to_delete, $enabled_providers ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$new_enabled_providers = array_diff( $old_enabled_providers, array( $provider_to_delete ) );
|
||||
$was_disabled = update_user_meta( $user_id, self::ENABLED_PROVIDERS_USER_META_KEY, $new_enabled_providers );
|
||||
$enabled_providers = array_diff( $enabled_providers, array( $provider_to_delete ) );
|
||||
|
||||
return (bool) $was_disabled;
|
||||
// Remove this from being a primary provider, if set.
|
||||
$primary_provider = self::get_primary_provider_for_user( $user_id );
|
||||
if ( $primary_provider && $primary_provider->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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backup code length for a user.
|
||||
*
|
||||
* @param WP_User $user User object.
|
||||
*
|
||||
* @return int Number of characters.
|
||||
*/
|
||||
private function get_backup_code_length( $user ) {
|
||||
/**
|
||||
* Customize the character count of the backup codes.
|
||||
*
|
||||
* @var int $code_length Length of the backup code.
|
||||
* @var WP_User $user User object.
|
||||
*/
|
||||
$code_length = (int) apply_filters( 'two_factor_backup_code_length', 8, $user );
|
||||
|
||||
return $code_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates backup codes & updates the user meta.
|
||||
*
|
||||
@ -239,8 +258,10 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
||||
$codes_hashed = (array) get_user_meta( $user->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 );
|
||||
|
||||
?>
|
||||
<p class="two-factor-prompt"><?php esc_html_e( 'Enter a recovery code.', 'two-factor' ); ?></p><br/>
|
||||
<p class="two-factor-prompt"><?php esc_html_e( 'Enter a recovery code.', 'two-factor' ); ?></p>
|
||||
<p>
|
||||
<label for="authcode"><?php esc_html_e( 'Recovery Code:', 'two-factor' ); ?></label>
|
||||
<input type="text" inputmode="numeric" name="two-factor-backup-code" id="authcode" class="input authcode" value="" size="20" pattern="[0-9 ]*" placeholder="1234 5678" data-digits="8" />
|
||||
<input type="text" inputmode="numeric" name="two-factor-backup-code" id="authcode" class="input authcode" value="" size="20" pattern="[0-9 ]*" placeholder="<?php echo esc_attr( $code_placeholder ); ?>" data-digits="<?php echo esc_attr( $code_length ); ?>" />
|
||||
</p>
|
||||
<?php
|
||||
submit_button( __( 'Submit', 'two-factor' ) );
|
||||
@ -399,4 +424,15 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
|
||||
// Update the backup code master list.
|
||||
update_user_meta( $user->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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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';
|
||||
?>
|
||||
<p class="two-factor-prompt"><?php esc_html_e( 'A verification code has been sent to the email address associated with your account.', 'two-factor' ); ?></p>
|
||||
<p>
|
||||
<label for="authcode"><?php esc_html_e( 'Verification Code:', 'two-factor' ); ?></label>
|
||||
<input type="text" inputmode="numeric" name="two-factor-email-code" id="authcode" class="input authcode" value="" size="20" pattern="[0-9 ]*" placeholder="1234 5678" data-digits="8" />
|
||||
<input type="text" inputmode="numeric" name="two-factor-email-code" id="authcode" class="input authcode" value="" size="20" pattern="[0-9 ]*" autocomplete="one-time-code" placeholder="<?php echo esc_attr( $token_placeholder ); ?>" data-digits="<?php echo esc_attr( $token_length ); ?>" />
|
||||
<?php submit_button( __( 'Log In', 'two-factor' ) ); ?>
|
||||
</p>
|
||||
<p class="two-factor-email-resend">
|
||||
@ -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 {
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user meta keys to delete during plugin uninstall.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function uninstall_user_meta_keys() {
|
||||
return array(
|
||||
self::TOKEN_META_KEY,
|
||||
self::TOKEN_META_KEY_TIMESTAMP,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -164,6 +164,10 @@ class Two_Factor_FIDO_U2F_Admin {
|
||||
* @param WP_User $user WP_User object of the logged-in user.
|
||||
*/
|
||||
public static function show_user_profile( $user ) {
|
||||
if ( ! Two_Factor_FIDO_U2F::is_supported_for_user( $user ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_nonce_field( "user_security_keys-{$user->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 );
|
||||
|
@ -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 {
|
||||
?>
|
||||
<p><?php esc_html_e( 'An error occurred while creating authentication data.', 'two-factor' ); ?></p>
|
||||
<?php
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
@ -388,4 +388,17 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user meta keys to delete during plugin uninstall.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function uninstall_user_meta_keys() {
|
||||
return array(
|
||||
self::REGISTERED_KEY_USER_META_KEY,
|
||||
self::AUTH_DATA_USER_META_KEY,
|
||||
'_two_factor_fido_u2f_register_request', // From Two_Factor_FIDO_U2F_Admin which is not loaded during uninstall.
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -123,6 +123,19 @@ abstract class Two_Factor_Provider {
|
||||
*/
|
||||
abstract public function is_available_for_user( $user );
|
||||
|
||||
/**
|
||||
* If this provider should be available for the user.
|
||||
*
|
||||
* @param WP_User|int $user WP_User object, user ID or null to resolve the current user.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_supported_for_user( $user = null ) {
|
||||
$providers = Two_Factor_Core::get_supported_providers_for_user( $user );
|
||||
|
||||
return isset( $providers[ static::class ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random eight-digit string to send out as an auth code.
|
||||
*
|
||||
@ -165,4 +178,24 @@ abstract class Two_Factor_Provider {
|
||||
|
||||
return (string) $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user meta keys that need to be deletated on plugin uninstall.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function uninstall_user_meta_keys() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the option keys that need to be deleted on plugin uninstall.
|
||||
*
|
||||
* Note: this method doesn't have access to the instantiated provider object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function uninstall_options() {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
||||
add_action( 'two_factor_user_options_' . __CLASS__, array( $this, 'user_two_factor_options' ) );
|
||||
|
||||
return parent::__construct();
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -110,7 +110,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
||||
* Returns the name of the provider.
|
||||
*/
|
||||
public function get_label() {
|
||||
return _x( 'Authenticator app', 'Provider Label', 'two-factor' );
|
||||
return _x( 'Authenticator App', 'Provider Label', 'two-factor' );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +119,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
||||
* @since 0.9.0
|
||||
*/
|
||||
public function get_alternative_provider_label() {
|
||||
return __( 'Use your authenticator app', 'two-factor' );
|
||||
return __( 'Use your authenticator app for time-based one-time passwords (TOTP)', 'two-factor' );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,7 +142,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
||||
/**
|
||||
* Rest API endpoint for handling deactivation of TOTP.
|
||||
*
|
||||
* @param WP_Rest_Request $request The Rest Request object.
|
||||
* @param WP_REST_Request $request The Rest Request object.
|
||||
* @return array Success array.
|
||||
*/
|
||||
public function rest_delete_totp( $request ) {
|
||||
@ -151,6 +151,10 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
||||
|
||||
$this->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 ) {
|
||||
@ -204,7 +208,7 @@ 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 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 = <?php echo wp_json_encode( __( 'Authenticator App QR Code', 'two-factor' ) ); ?>;
|
||||
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' );
|
||||
?>
|
||||
<input type="tel" name="two-factor-totp-authcode" id="two-factor-totp-authcode" class="input" value="" size="20" pattern="[0-9 ]*" placeholder="<?php echo esc_attr( $placeholder ); ?>" />
|
||||
<input type="text" inputmode="numeric" name="two-factor-totp-authcode" id="two-factor-totp-authcode" class="input" value="" size="20" pattern="[0-9 ]*" placeholder="<?php echo esc_attr( $placeholder ); ?>" autocomplete="off" />
|
||||
</label>
|
||||
<input type="submit" class="button totp-submit" name="two-factor-totp-submit" value="<?php esc_attr_e( 'Submit', 'two-factor' ); ?>" />
|
||||
</p>
|
||||
|
||||
<script>
|
||||
(function($){
|
||||
// Focus the auth code input when the checkbox is clicked.
|
||||
document.getElementById('enabled-Two_Factor_Totp').addEventListener('click', function(e) {
|
||||
if ( e.target.checked ) {
|
||||
document.getElementById('two-factor-totp-authcode').focus();
|
||||
}
|
||||
});
|
||||
|
||||
$('.totp-submit').click( function( e ) {
|
||||
e.preventDefault();
|
||||
var key = $('#two-factor-totp-key').val(),
|
||||
@ -359,6 +379,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
||||
user_id: <?php echo wp_json_encode( $user->ID ); ?>,
|
||||
key: key,
|
||||
code: code,
|
||||
enable_provider: true,
|
||||
}
|
||||
} ).fail( function( response, status ) {
|
||||
var errorMessage = response.responseJSON.message || status,
|
||||
@ -370,8 +391,10 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
||||
|
||||
$error.find('p').text( errorMessage );
|
||||
|
||||
$( '#enabled-Two_Factor_Totp' ).prop( 'checked', false );
|
||||
$('#two-factor-totp-authcode').val('');
|
||||
} ).then( function( response ) {
|
||||
$( '#enabled-Two_Factor_Totp' ).prop( 'checked', true );
|
||||
$( '#two-factor-totp-options' ).html( response.html );
|
||||
} );
|
||||
} );
|
||||
@ -398,6 +421,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
||||
user_id: <?php echo wp_json_encode( $user->ID ); ?>,
|
||||
}
|
||||
} ).then( function( response ) {
|
||||
$( '#enabled-Two_Factor_Totp' ).prop( 'checked', false );
|
||||
$( '#two-factor-totp-options' ).html( response.html );
|
||||
} );
|
||||
} );
|
||||
@ -666,7 +690,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
||||
</p>
|
||||
<p>
|
||||
<label for="authcode"><?php esc_html_e( 'Authentication Code:', 'two-factor' ); ?></label>
|
||||
<input type="text" inputmode="numeric" autocomplete="one-time-code" name="authcode" id="authcode" class="input authcode" value="" size="20" pattern="[0-9 ]*" placeholder="123 456" data-digits="<?php echo esc_attr( self::DEFAULT_DIGIT_COUNT ); ?>" />
|
||||
<input type="text" inputmode="numeric" autocomplete="one-time-code" name="authcode" id="authcode" class="input authcode" value="" size="20" pattern="[0-9 ]*" placeholder="123 456" autocomplete="one-time-code" data-digits="<?php echo esc_attr( self::DEFAULT_DIGIT_COUNT ); ?>" />
|
||||
</p>
|
||||
<script type="text/javascript">
|
||||
setTimeout( function(){
|
||||
@ -762,4 +786,16 @@ class Two_Factor_Totp extends Two_Factor_Provider {
|
||||
}
|
||||
return ( $a < $b ) ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user meta keys to delete during plugin uninstall.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function uninstall_user_meta_keys() {
|
||||
return array(
|
||||
self::SECRET_META_KEY,
|
||||
self::LAST_SUCCESSFUL_LOGIN_META_KEY,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
=== Two-Factor ===
|
||||
Contributors: georgestephanis, valendesigns, stevenkword, extendwings, sgrant, aaroncampbell, johnbillion, stevegrunwell, netweb, kasparsd, alihusnainarshad, passoniate
|
||||
Tags: two factor, two step, authentication, login, totp, fido u2f, u2f, email, backup codes, 2fa, yubikey
|
||||
Requires at least: 4.3
|
||||
Tested up to: 6.5
|
||||
Requires PHP: 5.6
|
||||
Stable tag: 0.9.1
|
||||
Tags: 2fa, mfa, totp, authentication, security
|
||||
Tested up to: 6.7
|
||||
Stable tag: 0.13.0
|
||||
License: GPL-2.0-or-later
|
||||
License URI: https://spdx.org/licenses/GPL-2.0-or-later.html
|
||||
|
||||
Enable Two-Factor Authentication using time-based one-time passwords (OTP, Google Authenticator), Universal 2nd Factor (FIDO U2F, YubiKey), email and backup verification codes.
|
||||
Enable Two-Factor Authentication (2FA) using time-based one-time passwords (TOTP), Universal 2nd Factor (U2F), email, and backup verification codes.
|
||||
|
||||
== Description ==
|
||||
|
||||
@ -25,9 +25,12 @@ For more history, see [this post](https://georgestephanis.wordpress.com/2013/08/
|
||||
Here is a list of action and filter hooks provided by the plugin:
|
||||
|
||||
- `two_factor_providers` filter overrides the available two-factor providers such as email and time-based one-time passwords. Array values are PHP classnames of the two-factor providers.
|
||||
- `two_factor_providers_for_user` filter overrides the available two-factor providers for a specific user. Array values are instances of provider classes and the user object `WP_User` is available as the second argument.
|
||||
- `two_factor_enabled_providers_for_user` filter overrides the list of two-factor providers enabled for a user. First argument is an array of enabled provider classnames as values, the second argument is the user ID.
|
||||
- `two_factor_user_authenticated` action which receives the logged in `WP_User` object as the first argument for determining the logged in user right after the authentication workflow.
|
||||
- `two_factor_token_ttl` filter overrides the time interval in seconds that an email token is considered after generation. Accepts the time in seconds as the first argument and the ID of the `WP_User` object being authenticated.
|
||||
- `two_factor_email_token_ttl` filter overrides the time interval in seconds that an email token is considered after generation. Accepts the time in seconds as the first argument and the ID of the `WP_User` object being authenticated.
|
||||
- `two_factor_email_token_length` filter overrides the default 8 character count for email tokens.
|
||||
- `two_factor_backup_code_length` filter overrides the default 8 character count for backup codes. Providers the `WP_User` of the associated user as the second argument.
|
||||
|
||||
== Frequently Asked Questions ==
|
||||
|
||||
|
@ -3,19 +3,23 @@
|
||||
* Two Factor
|
||||
*
|
||||
* @package Two_Factor
|
||||
* @author Plugin Contributors
|
||||
* @author WordPress.org Contributors
|
||||
* @copyright 2020 Plugin Contributors
|
||||
* @license GPL-2.0-or-later
|
||||
*
|
||||
* @wordpress-plugin
|
||||
* Plugin Name: Two Factor
|
||||
* Plugin URI: https://wordpress.org/plugins/two-factor/
|
||||
* Description: Two-Factor Authentication using time-based one-time passwords, Universal 2nd Factor (FIDO U2F), email and backup verification codes.
|
||||
* Author: Plugin Contributors
|
||||
* Version: 0.9.1
|
||||
* Description: Enable Two-Factor Authentication using time-based one-time passwords, Universal 2nd Factor (FIDO U2F, YubiKey), email, and backup verification codes.
|
||||
* Version: 0.13.0
|
||||
* Requires at least: 6.3
|
||||
* Requires PHP: 7.2
|
||||
* Author: WordPress.org Contributors
|
||||
* Author URI: https://github.com/wordpress/two-factor/graphs/contributors
|
||||
* Network: True
|
||||
* License: GPL-2.0-or-later
|
||||
* License URI: https://spdx.org/licenses/GPL-2.0-or-later.html
|
||||
* Text Domain: two-factor
|
||||
* Network: True
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -26,7 +30,7 @@ define( 'TWO_FACTOR_DIR', plugin_dir_path( __FILE__ ) );
|
||||
/**
|
||||
* Version of the plugin.
|
||||
*/
|
||||
define( 'TWO_FACTOR_VERSION', '0.9.1' );
|
||||
define( 'TWO_FACTOR_VERSION', '0.13.0' );
|
||||
|
||||
/**
|
||||
* Include the base class here, so that other plugins can also extend it.
|
||||
@ -39,10 +43,13 @@ require_once TWO_FACTOR_DIR . 'providers/class-two-factor-provider.php';
|
||||
require_once TWO_FACTOR_DIR . 'class-two-factor-core.php';
|
||||
|
||||
/**
|
||||
* A compatability layer for some of the most-used plugins out there.
|
||||
* A compatibility layer for some of the most-used plugins out there.
|
||||
*/
|
||||
require_once TWO_FACTOR_DIR . 'class-two-factor-compat.php';
|
||||
|
||||
$two_factor_compat = new Two_Factor_Compat();
|
||||
|
||||
Two_Factor_Core::add_hooks( $two_factor_compat );
|
||||
|
||||
// Delete our options and user meta during uninstall.
|
||||
register_uninstall_hook( __FILE__, array( Two_Factor_Core::class, 'uninstall' ) );
|
||||
|
@ -1,41 +1,8 @@
|
||||
|
||||
.two-factor-methods-table {
|
||||
background-color: #fff;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.two-factor-methods-table thead,
|
||||
.two-factor-methods-table tfoot {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.two-factor-methods-table thead th {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.two-factor-methods-table .col-primary,
|
||||
.two-factor-methods-table .col-enabled {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
.two-factor-methods-table .col-name {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.two-factor-methods-table tbody th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.two-factor-methods-table tbody th,
|
||||
.two-factor-methods-table tbody td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.two-factor-methods-table tbody tr:nth-child(odd) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.two-factor-methods-table .two-factor-method-label {
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
|
Loading…
x
Reference in New Issue
Block a user