Skip to content

Commit 2b0d9bc

Browse files
authored
Add method to disable an individual provider (#587)
1 parent 5fe5af3 commit 2b0d9bc

File tree

2 files changed

+91
-1
lines changed

2 files changed

+91
-1
lines changed

class-two-factor-core.php

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public static function add_hooks( $compat ) {
125125
add_filter( 'authenticate', array( __CLASS__, 'filter_authenticate_block_cookies' ), PHP_INT_MAX );
126126

127127
add_filter( 'attach_session_information', array( __CLASS__, 'filter_session_information' ), 10, 2 );
128-
128+
129129
add_action( 'admin_init', array( __CLASS__, 'trigger_user_settings_action' ) );
130130
add_filter( 'two_factor_providers', array( __CLASS__, 'enable_dummy_method_for_debug' ) );
131131

@@ -1788,6 +1788,8 @@ public static function user_two_factor_options( $user ) {
17881788
/**
17891789
* Enable a provider for a user.
17901790
*
1791+
* The caller is responsible for checking the user has permission to do this.
1792+
*
17911793
* @param int $user_id The ID of the user.
17921794
* @param string $new_provider The name of the provider class.
17931795
*
@@ -1820,6 +1822,39 @@ public static function enable_provider_for_user( $user_id, $new_provider ) {
18201822
return $enabled && $has_primary;
18211823
}
18221824

1825+
/**
1826+
* Disable a provider for a user.
1827+
*
1828+
* This intentionally doesn't set a new primary provider when disabling the current primary provider, because
1829+
* `get_primary_provider_for_user()` will pick a new one automatically.
1830+
*
1831+
* The caller is responsible for checking the user has permission to do this.
1832+
*
1833+
* @param int $user_id The ID of the user.
1834+
* @param string $provider The name of the provider class.
1835+
*
1836+
* @return bool True if the provider was disabled, false otherwise.
1837+
*/
1838+
public static function disable_provider_for_user( $user_id, $provider_to_delete ) {
1839+
$is_registered = array_key_exists( $provider_to_delete, self::get_providers() );
1840+
1841+
if ( ! $is_registered ) {
1842+
return false;
1843+
}
1844+
1845+
$old_enabled_providers = self::get_enabled_providers_for_user( $user_id );
1846+
$is_enabled = in_array( $provider_to_delete, $old_enabled_providers );
1847+
1848+
if ( ! $is_enabled ) {
1849+
return true;
1850+
}
1851+
1852+
$new_enabled_providers = array_diff( $old_enabled_providers, array( $provider_to_delete ) );
1853+
$was_disabled = update_user_meta( $user_id, self::ENABLED_PROVIDERS_USER_META_KEY, $new_enabled_providers );
1854+
1855+
return (bool) $was_disabled;
1856+
}
1857+
18231858
/**
18241859
* Update the user meta value.
18251860
*

tests/class-two-factor-core.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,61 @@ public function test_show_password_reset_error() {
811811
$this->assertStringContainsString( 'check your email for instructions on regaining access', $contents );
812812
}
813813

814+
/**
815+
* @covers Two_Factor_Core::enable_provider_for_user()
816+
* @covers Two_Factor_Core::disable_provider_for_user()
817+
*/
818+
public function test_enable_disable_provider_for_user() {
819+
$user = self::factory()->user->create_and_get();
820+
$enabled_providers = Two_Factor_Core::get_enabled_providers_for_user( $user->ID );
821+
$this->assertEmpty( $enabled_providers );
822+
823+
// Disabling one that's already disabled should succeed.
824+
$totp_disabled = Two_Factor_Core::disable_provider_for_user( $user->ID, 'Two_Factor_Totp' );
825+
$this->assertTrue( $totp_disabled );
826+
827+
// Disabling one that doesn't exist should fail.
828+
$nonexistent_enabled = Two_Factor_Core::enable_provider_for_user( $user->ID, 'Nonexistent_Provider' );
829+
$enabled_providers = Two_Factor_Core::get_enabled_providers_for_user( $user->ID );
830+
$this->assertFalse( $nonexistent_enabled );
831+
$this->assertEmpty( $enabled_providers );
832+
$this->assertNull( Two_Factor_Core::get_primary_provider_for_user( $user->ID ) );
833+
834+
// Enabling a valid one should succeed. The first one that's enabled and configured should be the default primary.
835+
$totp = Two_Factor_Totp::get_instance();
836+
$totp->set_user_totp_key( $user->ID, 'foo' );
837+
$totp_enabled = Two_Factor_Core::enable_provider_for_user( $user->ID, 'Two_Factor_Totp' );
838+
$enabled_providers = Two_Factor_Core::get_enabled_providers_for_user( $user->ID );
839+
$this->assertTrue( $totp_enabled );
840+
$this->assertSame( array( 'Two_Factor_Totp' ), $enabled_providers );
841+
$this->assertSame( 'Two_Factor_Totp', Two_Factor_Core::get_primary_provider_for_user( $user->ID )->get_key() );
842+
843+
// Enabling one that's already enabled should succeed.
844+
$totp_enabled = Two_Factor_Core::enable_provider_for_user( $user->ID, 'Two_Factor_Totp' );
845+
$this->assertTrue( $totp_enabled );
846+
847+
// Enabling another should succeed, and not change the primary.
848+
$dummy_enabled = Two_Factor_Core::enable_provider_for_user( $user->ID, 'Two_Factor_Dummy' );
849+
$enabled_providers = Two_Factor_Core::get_enabled_providers_for_user( $user->ID );
850+
$this->assertTrue( $dummy_enabled );
851+
$this->assertSame( array( 'Two_Factor_Totp', 'Two_Factor_Dummy' ), $enabled_providers );
852+
$this->assertSame( 'Two_Factor_Totp', Two_Factor_Core::get_primary_provider_for_user( $user->ID )->get_key() );
853+
854+
// Disabling one that doesn't exist should fail.
855+
$nonexistent_disabled = Two_Factor_Core::disable_provider_for_user( $user->ID, 'Nonexistent_Provider' );
856+
$enabled_providers = Two_Factor_Core::get_enabled_providers_for_user( $user->ID );
857+
$this->assertFalse( $nonexistent_disabled );
858+
$this->assertSame( array( 'Two_Factor_Totp', 'Two_Factor_Dummy' ), $enabled_providers );
859+
$this->assertSame( 'Two_Factor_Totp', Two_Factor_Core::get_primary_provider_for_user( $user->ID )->get_key() );
860+
861+
// Disabling one that's enabled should succeed, and change the primary to the next available one.
862+
$totp_disabled = Two_Factor_Core::disable_provider_for_user( $user->ID, 'Two_Factor_Totp' );
863+
$enabled_providers = Two_Factor_Core::get_enabled_providers_for_user( $user->ID );
864+
$this->assertTrue( $totp_disabled ); //todo enable and fix
865+
$this->assertSame( array( 1 => 'Two_Factor_Dummy' ), $enabled_providers );
866+
$this->assertSame( 'Two_Factor_Dummy', Two_Factor_Core::get_primary_provider_for_user( $user->ID )->get_key() );
867+
}
868+
814869
/**
815870
* Ensure that when a user enables two factor, that they are able to continue to change settings.
816871
*

0 commit comments

Comments
 (0)