File manager - Edit - /home/xfekoga/grenier/wp-content/plugins/defender-security/src/component/two-factor/providers/class-backup-codes.php
Back
<?php /** * Provider for two-factor authentication using backup codes. * * @package WP_Defender\Component\Two_Factor\Providers */ namespace WP_Defender\Component\Two_Factor\Providers; use WP_User; use WP_Error; use WP_Defender\Component\Two_Factor\Two_Factor_Provider; /** * Manage backup codes for two-factor authentication. * * @since 2.8.0 */ class Backup_Codes extends Two_Factor_Provider { /** * 2fa provider slug. * * @var string */ public static $slug = 'backup-codes'; /** * The number backup codes. * * @var int */ public const BACKUP_CODE_COUNT = 5; /** * The length of each backup code. * * @var int */ public const BACKUP_CODE_SIZE = 8; /** * Characters used to generate the backup codes. * * @var string */ public const BACKUP_CODE_CHARACTERS = '1234567890'; /** * The user meta key for backup codes. * * @var string */ public const BACKUP_CODE_VALUES = 'wd_2fa_backup_codes'; /** * Meta key to check if backup codes are activated for the user. * * @var string */ public const BACKUP_CODE_START = 'wd_2fa_backup_codes_is_activated'; /** * Indicates whether backup codes are activated for the user. * * @var string */ protected $is_activated; /** * Description of the backup codes' provider. * * @var string */ protected $description; /** * Constructor that initializes actions. */ public function __construct() { add_action( 'admin_notices', array( $this, 'admin_notices' ) ); add_action( 'wd_2fa_init_provider_' . self::$slug, array( &$this, 'init_provider' ) ); add_action( 'wd_2fa_user_options_' . self::$slug, array( &$this, 'user_options' ) ); } /** * Initializes the provider for a user. * * @param WP_User $user The user object. */ public function init_provider( WP_User $user ): void { $this->is_activated = get_user_meta( $user->ID, self::BACKUP_CODE_START, true ); $this->description = empty( $this->is_activated ) ? esc_html__( 'Generate non-expirable backup codes that can be used to log in once.', 'defender-security' ) : esc_html__( 'Each backup code can only be used to log in once.', 'defender-security' ); } /** * Get the name of the provider. * * @return string */ public function get_label(): string { return esc_html__( 'Backup Codes', 'defender-security' ); } /** * Returns the label for the backup codes' provider. * * @return string The label. */ public function get_login_label(): string { return $this->get_label(); } /** * Returns the login label for the backup codes' provider. * * @return string The login label. */ public function get_user_label(): string { return esc_html__( 'Backup codes', 'defender-security' ); } /** * Get the desc of the provider. * * @return string */ public function get_description(): string { return $this->description; } /** * Whether this 2FA provider is configured and codes are available for the user specified. * * @param WP_User $user The user object. * * @return bool */ public function is_available_for_user( WP_User $user ): bool { // Does this user have available codes? if ( 0 < self::get_unused_codes_for_user( $user ) ) { return true; } return false; } /** * Displays the authentication form for backup codes. */ public function authentication_form(): void { ?> <p class="wpdef-2fa-label"><?php echo $this->get_login_label(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p> <p class="wpdef-2fa-text"> <?php echo esc_html__( 'Enter one of your recovery codes to log in to your account.', 'defender-security' ); ?> </p> <input type="text" autofocus value="" autocomplete="off" name="backup-codes"> <button class="button button-primary float-r" type="submit"><?php esc_attr_e( 'Authenticate', 'defender-security' ); ?></button> <?php } /** * Return the number of unused codes for the specified user. * * @param WP_User $user The user object. * * @return int */ public static function get_unused_codes_for_user( WP_User $user ): int { $backup_codes = get_user_meta( $user->ID, self::BACKUP_CODE_VALUES, true ); if ( is_array( $backup_codes ) && ! empty( $backup_codes ) ) { return count( $backup_codes ); } return 0; } /** * Generate a random string for an auth code. * * @return string */ private static function get_code(): string { $code = ''; $chars = self::BACKUP_CODE_CHARACTERS; for ( $i = 0; $i < self::BACKUP_CODE_SIZE; $i++ ) { $code .= substr( $chars, wp_rand( 0, strlen( $chars ) - 1 ), 1 ); } return $code; } /** * Show an admin notice when backup codes have run out. * * @return void */ public function admin_notices(): void { $user = wp_get_current_user(); if ( ! is_object( $user ) ) { return; } // Is User role from common list checked? if ( empty( array_intersect( $user->roles, $this->get_model()->user_roles ) ) ) { return; } // Return if the provider is not enabled. if ( ! in_array( self::$slug, $this->get_component()->get_enabled_providers_for_user( $user->ID ), true ) ) { return; } // Return if we are not out of codes. if ( $this->is_available_for_user( $user ) ) { return; } ?> <div class="error"> <p> <span> <?php printf( /* translators: %s: URL to regenerate code */ esc_html__( 'You\'ve used all generated backup codes. Please %s new codes accordingly.', 'defender-security' ), '<a href="' . esc_url( get_edit_user_link( $user->ID ) . '#wpdef-2fa-backup-codes' ) . '">' . esc_html__( 'generate', 'defender-security' ) . '</a>' ); ?> <span> </p> </div> <?php } /** * Display auth method. * * @param WP_User $user The user object. * * @return void */ public function user_options( WP_User $user ): void { $cond = $this->get_component()->is_checked_enabled_provider_by_slug( $user, self::$slug ) && $this->is_available_for_user( $user ); $attr = $cond ? '' : ' disabled'; // Check whether codes were generated before or not. if ( empty( $this->is_activated ) ) { $button_text = esc_html__( 'Generate Backup Codes', 'defender-security' ); $class = 'hidden'; $count = 0; $show_notice = 'no'; } else { $button_text = esc_html__( 'Get New Codes', 'defender-security' ); $class = $cond ? '' : 'hidden'; $count = self::get_unused_codes_for_user( $user ); $show_notice = 0 === $count ? 'yes' : 'no'; } $filename = str_replace( array( 'http://www.', 'http://', 'https://www.', 'https://', ), '', get_bloginfo( 'url' ) ); $filename = str_replace( '/', '-', $filename ); $filename = sanitize_file_name( $filename . '-backup-codes.txt' ); $this->get_controller()->render_partial( 'two-fa/providers/backup-codes', array( 'number_of_codes' => self::display_number_of_codes( $count ), 'url' => $this->get_url( 'generate_backup_codes' ), 'user' => $user, 'attr' => $attr, 'class' => $class, 'show_notice' => $show_notice, 'button_text' => $button_text, 'filename' => $filename, ) ); } /** * Generate backup codes. * * @param WP_User $user The user object. * @param array $args Optional arguments to customize code generation. * * @return array */ public static function generate_codes( WP_User $user, array $args = array() ): array { $codes = array(); $codes_hashed = array(); // Check for arguments. if ( isset( $args['number'] ) ) { $num_codes = (int) $args['number']; } else { $num_codes = self::BACKUP_CODE_COUNT; } // Add a flag if it haven't existed yet. if ( empty( get_user_meta( $user->ID, self::BACKUP_CODE_START, true ) ) ) { update_user_meta( $user->ID, self::BACKUP_CODE_START, 1 ); } if ( isset( $args['method'] ) && 'append' === $args['method'] ) { $codes_hashed = (array) get_user_meta( $user->ID, self::BACKUP_CODE_VALUES, true ); } for ( $i = 0; $i < $num_codes; $i++ ) { $code = self::get_code(); $codes_hashed[] = wp_hash_password( $code ); $codes[] = $code; unset( $code ); } update_user_meta( $user->ID, self::BACKUP_CODE_VALUES, $codes_hashed ); // Unhashed values. return $codes; } /** * Delete codes. * * @param WP_User $user The user object. * @param string $code_hashed The hashed code to delete. * * @return void */ public function delete_code( WP_User $user, string $code_hashed ): void { $backup_codes = get_user_meta( $user->ID, self::BACKUP_CODE_VALUES, true ); // Delete the current code from the list since it's been used. $backup_codes = array_flip( $backup_codes ); unset( $backup_codes[ $code_hashed ] ); $backup_codes = array_values( array_flip( $backup_codes ) ); // Update the backup code master list. update_user_meta( $user->ID, self::BACKUP_CODE_VALUES, $backup_codes ); } /** * Displays the number of unused codes. * * @param int $count The number of unused codes. * * @return string */ public static function display_number_of_codes( int $count ): string { return sprintf( /* translators: %s: count */ _n( '%s unused code remaining', '%s unused codes remaining', $count, 'defender-security' ), $count ); } /** * Validates a backup code entered by the user. * * @param WP_User $user The user object. * @param string $code The backup code to validate. * * @return bool|WP_Error */ protected function validate_code( WP_User $user, string $code ) { $backup_codes = get_user_meta( $user->ID, self::BACKUP_CODE_VALUES, true ); if ( is_array( $backup_codes ) && ! empty( $backup_codes ) ) { foreach ( $backup_codes as $code_hashed ) { if ( wp_check_password( $code, $code_hashed, $user->ID ) ) { $this->delete_code( $user, $code_hashed ); return true; } } } $lockout_message = $this->get_component()->verify_attempt( $user->ID, self::$slug ); return new WP_Error( 'opt_fail', empty( $lockout_message ) ? esc_html__( 'ERROR: Invalid verification code.', 'defender-security' ) : $lockout_message ); } /** * Validates the authentication for a user using a backup code. * * @param WP_User $user The user object. * * @return bool|WP_Error True if authenticated, WP_Error if not. */ public function validate_authentication( WP_User $user ) { $codes = defender_get_data_from_request( 'backup-codes', 'p' ) ?? false; if ( false === $codes ) { return new WP_Error( 'opt_fail', esc_html__( 'ERROR: Invalid backup code.', 'defender-security' ) ); } return $this->validate_code( $user, $codes ); } }
| ver. 1.4 |
Github
|
.
| PHP 8.0.30 | Generation time: 0 |
proxy
|
phpinfo
|
Settings