Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a filter to filter the classname used for a provider #546

Merged
merged 11 commits into from
Apr 12, 2023
Merged
57 changes: 33 additions & 24 deletions class-two-factor-core.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,17 +179,27 @@ public static function get_providers() {
/**
* For each filtered provider,
*/
foreach ( $providers as $class => $path ) {
foreach ( $providers as $provider_key => $path ) {
include_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 before instantiating.
*/
if ( class_exists( $class ) ) {
try {
$providers[ $class ] = call_user_func( array( $class, 'get_instance' ) );
$providers[ $provider_key ] = call_user_func( array( $class, 'get_instance' ) );
} catch ( Exception $e ) {
unset( $providers[ $class ] );
unset( $providers[ $provider_key ] );
}
}
}
Expand Down Expand Up @@ -411,9 +421,9 @@ public static function get_available_providers_for_user( $user = null ) {
$enabled_providers = self::get_enabled_providers_for_user( $user );
$configured_providers = array();

foreach ( $providers as $classname => $provider ) {
if ( in_array( $classname, $enabled_providers, true ) && $provider->is_available_for_user( $user ) ) {
$configured_providers[ $classname ] = $provider;
foreach ( $providers as $provider_key => $provider ) {
if ( in_array( $provider_key, $enabled_providers, true ) && $provider->is_available_for_user( $user ) ) {
$configured_providers[ $provider_key ] = $provider;
}
}

Expand Down Expand Up @@ -712,10 +722,9 @@ public static function login_html( $user, $login_nonce, $redirect_to, $error_msg
$provider = call_user_func( array( $provider, 'get_instance' ) );
}

$provider_class = get_class( $provider );

$provider_key = $provider->get_key();
$available_providers = self::get_available_providers_for_user( $user );
$backup_providers = array_diff_key( $available_providers, array( $provider_class => null ) );
$backup_providers = array_diff_key( $available_providers, array( $provider_key => null ) );
$interim_login = isset( $_REQUEST['interim-login'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended

$rememberme = intval( self::rememberme() );
Expand All @@ -735,7 +744,7 @@ public static function login_html( $user, $login_nonce, $redirect_to, $error_msg
?>

<form name="validate_2fa_form" id="loginform" action="<?php echo esc_url( self::login_url( array( 'action' => 'validate_2fa' ), 'login_post' ) ); ?>" method="post" autocomplete="off">
<input type="hidden" name="provider" id="provider" value="<?php echo esc_attr( $provider_class ); ?>" />
<input type="hidden" name="provider" id="provider" value="<?php echo esc_attr( $provider_key ); ?>" />
<input type="hidden" name="wp-auth-id" id="wp-auth-id" value="<?php echo esc_attr( $user->ID ); ?>" />
<input type="hidden" name="wp-auth-nonce" id="wp-auth-nonce" value="<?php echo esc_attr( $login_nonce ); ?>" />
<?php if ( $interim_login ) { ?>
Expand All @@ -750,12 +759,12 @@ public static function login_html( $user, $login_nonce, $redirect_to, $error_msg

<?php
if ( 1 === count( $backup_providers ) ) :
$backup_classname = key( $backup_providers );
$backup_provider = $backup_providers[ $backup_classname ];
$login_url = self::login_url(
$backup_provider_key = key( $backup_providers );
$backup_provider = $backup_providers[ $backup_provider_key ];
$login_url = self::login_url(
array(
'action' => 'validate_2fa',
'provider' => $backup_classname,
'provider' => $backup_provider_key,
'wp-auth-id' => $user->ID,
'wp-auth-nonce' => $login_nonce,
'redirect_to' => $redirect_to,
Expand Down Expand Up @@ -787,11 +796,11 @@ public static function login_html( $user, $login_nonce, $redirect_to, $error_msg
</p>
<ul class="backup-methods">
<?php
foreach ( $backup_providers as $backup_classname => $backup_provider ) :
foreach ( $backup_providers as $backup_provider_key => $backup_provider ) :
$login_url = self::login_url(
array(
'action' => 'validate_2fa',
'provider' => $backup_classname,
'provider' => $backup_provider_key,
'wp-auth-id' => $user->ID,
'wp-auth-nonce' => $login_nonce,
'redirect_to' => $redirect_to,
Expand Down Expand Up @@ -1428,7 +1437,7 @@ public static function user_two_factor_options( $user ) {
$primary_provider = self::get_primary_provider_for_user( $user->ID );

if ( ! empty( $primary_provider ) && is_object( $primary_provider ) ) {
$primary_provider_key = get_class( $primary_provider );
$primary_provider_key = $primary_provider->get_key();
} else {
$primary_provider_key = null;
}
Expand All @@ -1452,24 +1461,24 @@ public static function user_two_factor_options( $user ) {
</tr>
</thead>
<tbody>
<?php foreach ( self::get_providers() as $class => $object ) : ?>
<?php foreach ( self::get_providers() as $provider_key => $object ) : ?>
<tr>
<th scope="row"><input id="enabled-<?php echo esc_attr( $class ); ?>" type="checkbox" name="<?php echo esc_attr( self::ENABLED_PROVIDERS_USER_META_KEY ); ?>[]" value="<?php echo esc_attr( $class ); ?>" <?php checked( in_array( $class, $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( $class ); ?>" <?php checked( $class, $primary_provider_key ); ?> /></th>
<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( $class ); ?>"><?php echo esc_html( $object->get_label() ); ?></label>
<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_' . $class } hook instead.
* 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-' . $class, array( $user ), '0.7.0', 'two_factor_user_options_' . $class );
do_action( 'two_factor_user_options_' . $class, $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>
Expand Down
14 changes: 0 additions & 14 deletions providers/class-two-factor-backup-codes.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,6 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
*/
const NUMBER_OF_CODES = 10;

/**
* Ensures only one instance of this class exists in memory at any one time.
*
* @since 0.1-dev
*/
public static function get_instance() {
static $instance;
$class = __CLASS__;
if ( ! is_a( $instance, $class ) ) {
$instance = new $class();
}
return $instance;
}

/**
* Class constructor.
*
Expand Down
14 changes: 0 additions & 14 deletions providers/class-two-factor-dummy.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,6 @@
*/
class Two_Factor_Dummy extends Two_Factor_Provider {

/**
* Ensures only one instance of this class exists in memory at any one time.
*
* @since 0.1-dev
*/
public static function get_instance() {
static $instance;
$class = __CLASS__;
if ( ! is_a( $instance, $class ) ) {
$instance = new $class();
}
return $instance;
}

/**
* Class constructor.
*
Expand Down
14 changes: 0 additions & 14 deletions providers/class-two-factor-email.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,6 @@ class Two_Factor_Email extends Two_Factor_Provider {
*/
const INPUT_NAME_RESEND_CODE = 'two-factor-email-code-resend';

/**
* Ensures only one instance of this class exists in memory at any one time.
*
* @since 0.1-dev
*/
public static function get_instance() {
static $instance;
$class = __CLASS__;
if ( ! is_a( $instance, $class ) ) {
$instance = new $class();
}
return $instance;
}

/**
* Class constructor.
*
Expand Down
15 changes: 0 additions & 15 deletions providers/class-two-factor-fido-u2f.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,6 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
*/
const U2F_ASSET_VERSION = '0.2.1';

/**
* Ensures only one instance of this class exists in memory at any one time.
*
* @return \Two_Factor_FIDO_U2F
*/
public static function get_instance() {
static $instance;

if ( ! isset( $instance ) ) {
$instance = new self();
}

return $instance;
}

/**
* Class constructor.
*
Expand Down
28 changes: 28 additions & 0 deletions providers/class-two-factor-provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@
*/
abstract class Two_Factor_Provider {

/**
* Ensures only one instance of the provider class exists in memory at any one time.
*
* @since 0.1-dev
*/
public static function get_instance() {
static $instances = array();

$class_name = static::class;

if ( ! isset( $instances[ $class_name ] ) ) {
$instances[ $class_name ] = new $class_name;
}

return $instances[ $class_name ];
}

/**
* Class constructor.
*
Expand Down Expand Up @@ -41,6 +58,17 @@ public function print_label() {
echo esc_html( $this->get_label() );
}

/**
* Retrieves the provider key / slug.
*
* @since 0.9.0
*
* @return string
*/
public function get_key() {
return get_class( $this );
}

/**
* Prints the form that prompts the user to authenticate.
*
Expand Down
13 changes: 0 additions & 13 deletions providers/class-two-factor-totp.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,6 @@ class Two_Factor_Totp extends Two_Factor_Provider {
*/
private static $base_32_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';

/**
* Ensures only one instance of this class exists in memory at any one time.
*
* @codeCoverageIgnore
*/
public static function get_instance() {
static $instance;
if ( ! isset( $instance ) ) {
$instance = new self();
}
return $instance;
}

/**
* Class constructor. Sets up hooks, etc.
*
Expand Down
30 changes: 30 additions & 0 deletions tests/class-secure-dummy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
/**
* Class for creating a dummy provider that never passes.
*
* This is a mock for unit testing the provider class name filter, and where authentication should never pass.
*
* @package Two_Factor
*/
class Two_Factor_Dummy_Secure extends Two_Factor_Dummy {

/**
* Pretend to be the Two_Factor_Dummy provider.
*/
public function get_key() {
return 'Two_Factor_Dummy';
}

/**
* Validates the users input token.
*
* In this class we just return false.
*
* @param WP_User $user WP_User object of the logged-in user.
* @return boolean
*/
public function validate_authentication( $user ) {
return false;
}

}
Loading