updated plugin Two Factor version 0.13.0

This commit is contained in:
2025-04-29 21:20:04 +00:00
committed by Gitium
parent c950632407
commit a212704ec2
16 changed files with 547 additions and 231 deletions

View File

@ -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 '<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(
'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(
'<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>',
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' ) .
' <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' );
}
?>
<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>
<tbody>
<?php foreach ( self::get_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>
<td>
<label class="two-factor-method-label" for="enabled-<?php echo esc_attr( $provider_key ); ?>"><?php echo esc_html( $object->get_label() ); ?></label>
<?php
/**
* Fires after user options are shown.
*
* Use the {@see 'two_factor_user_options_' . $provider_key } hook instead.
*
* @deprecated 0.7.0
*
* @param WP_User $user The user.
*/
do_action_deprecated( 'two-factor-user-options-' . $provider_key, array( $user ), '0.7.0', 'two_factor_user_options_' . $provider_key );
do_action( 'two_factor_user_options_' . $provider_key, $user );
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</td>
</tr>
</table>
</fieldset>
<?php
<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 );
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 two-factor-methods-table" role="presentation">
<tbody>
<?php foreach ( $providers as $provider_key => $object ) : ?>
<tr>
<th><?php echo esc_html( $object->get_label() ); ?></th>
<td>
<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.
*
* Use the {@see 'two_factor_user_options_' . $provider_key } hook instead.
*
* @deprecated 0.7.0
*
* @param WP_User $user The user.
*/
do_action_deprecated( 'two-factor-user-options-' . $provider_key, array( $user ), '0.7.0', 'two_factor_user_options_' . $provider_key );
do_action( 'two_factor_user_options_' . $provider_key, $user );
?>
</td>
</tr>
<?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>
<?php
}
/**
@ -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.