init_hooks(); } /** * Initialize WordPress hooks. * * @since 0.1.0 */ private function init_hooks(): void { add_filter( 'authenticate', array( $this, 'check_user_requirements' ), 30, 1 ); add_action( 'admin_notices', array( $this, 'display_user_notices' ) ); add_action( 'two_factor_extended_daily_check', array( $this, 'check_all_users_compliance' ) ); } /** * Check user's 2FA requirements on login. * * @since 0.1.0 * * @param WP_User|WP_Error|null $user User object or error. * * @return WP_User|WP_Error|null User object or error if requirements not met. */ public function check_user_requirements( $user ) { // Skip if not a user object. if ( ! $user instanceof WP_User ) { return $user; } // Skip if Two Factor not available. if ( ! class_exists( 'Two_Factor_Core' ) ) { return $user; } // Get required providers for user. $required_providers = $this->get_required_providers_for_user( $user->ID ); if ( empty( $required_providers ) ) { return $user; } // Check if user is in grace period. if ( $this->is_in_grace_period( $user->ID ) ) { $this->set_grace_period_notice( $user->ID ); return $user; } // Check if user meets requirements. if ( ! $this->user_meets_requirements( $user->ID, $required_providers ) ) { return new WP_Error( 'two_factor_extended_required', sprintf( /* translators: %s: Required providers list */ __( 'Your account requires the following 2FA methods to be configured: %s. Please contact an administrator for assistance.', 'two-factor-extended' ), implode( ', ', $this->get_provider_labels( $required_providers ) ) ) ); } return $user; } /** * Get required providers for user based on their roles. * * @since 0.1.0 * * @param int $user_id User ID. * * @return array Array of required provider class names. */ public function get_required_providers_for_user( int $user_id ): array { $required = array(); // Check if user is super admin (Multisite). if ( is_multisite() && Two_Factor_Extended_Role_Manager::is_super_admin( $user_id ) ) { $network_settings = get_site_option( TWO_FACTOR_EXTENDED_NETWORK_OPTION_SETTINGS, array() ); if ( ! is_array( $network_settings ) ) { $network_settings = array(); } if ( isset( $network_settings['super_admin_requirements'] ) && is_array( $network_settings['super_admin_requirements'] ) ) { foreach ( $network_settings['super_admin_requirements'] as $provider ) { if ( is_string( $provider ) ) { $required[] = $provider; } } } } // Check network-wide settings (Multisite). if ( is_multisite() ) { $network_settings = get_site_option( TWO_FACTOR_EXTENDED_NETWORK_OPTION_SETTINGS, array() ); if ( ! is_array( $network_settings ) ) { $network_settings = array(); } if ( ! empty( $network_settings['enforce_network_wide'] ) ) { // Network enforcement is enabled. $user_roles = Two_Factor_Extended_Role_Manager::get_user_roles( $user_id ); if ( isset( $network_settings['role_requirements'] ) && is_array( $network_settings['role_requirements'] ) ) { foreach ( $user_roles as $role ) { if ( isset( $network_settings['role_requirements'][ $role ] ) && is_array( $network_settings['role_requirements'][ $role ] ) ) { foreach ( $network_settings['role_requirements'][ $role ] as $provider ) { if ( is_string( $provider ) ) { $required[] = $provider; } } } } } // If site override is not allowed, return only network requirements. if ( empty( $network_settings['allow_site_override'] ) ) { $network_required = array_values( array_unique( $required ) ); // Intersect with globally-available providers (Two Factor 0.16+). if ( class_exists( 'Two_Factor_Core' ) ) { $available = array_keys( Two_Factor_Core::get_providers() ); $network_required = ! empty( $available ) ? array_values( array_intersect( $network_required, $available ) ) : $network_required; } return $network_required; } } } // Get site-level requirements. $settings = get_option( TWO_FACTOR_EXTENDED_OPTION_SETTINGS, array() ); if ( ! is_array( $settings ) ) { $settings = array(); } $user_roles = Two_Factor_Extended_Role_Manager::get_user_roles( $user_id ); if ( ! empty( $user_roles ) && isset( $settings['role_requirements'] ) && is_array( $settings['role_requirements'] ) ) { foreach ( $user_roles as $role ) { if ( isset( $settings['role_requirements'][ $role ] ) ) { $role_requirements = $settings['role_requirements'][ $role ]; if ( is_array( $role_requirements ) ) { foreach ( $role_requirements as $provider ) { if ( is_string( $provider ) ) { $required[] = $provider; } } } } } } $required = array_values( array_unique( $required ) ); // Intersect with globally-available providers (Two Factor 0.16+). // If Two-Factor has disabled a provider site-wide, it cannot be enforced. if ( class_exists( 'Two_Factor_Core' ) ) { $available = array_keys( Two_Factor_Core::get_providers() ); if ( ! empty( $available ) ) { $required = array_values( array_intersect( $required, $available ) ); } } return $required; } /** * Check if user meets 2FA requirements. * * @since 0.1.0 * * @param int $user_id User ID. * @param array $required_providers Required provider class names. * * @return bool True if user meets requirements, false otherwise. */ public function user_meets_requirements( int $user_id, array $required_providers ): bool { if ( empty( $required_providers ) ) { return true; } $enabled_providers = Two_Factor_Extended_Provider_Detector::get_user_enabled_providers( $user_id ); foreach ( $required_providers as $provider_class ) { if ( ! isset( $enabled_providers[ $provider_class ] ) ) { return false; } } return true; } /** * Check if user is in grace period. * * @since 0.1.0 * * @param int $user_id User ID. * * @return bool True if in grace period, false otherwise. */ public function is_in_grace_period( int $user_id ): bool { $settings = get_option( TWO_FACTOR_EXTENDED_OPTION_SETTINGS, array() ); if ( ! is_array( $settings ) ) { $settings = array(); } $grace_days = isset( $settings['grace_period_days'] ) && is_numeric( $settings['grace_period_days'] ) ? (int) $settings['grace_period_days'] : 0; // No grace period configured. if ( 0 === $grace_days ) { return false; } $start_date = get_user_meta( $user_id, self::META_ENFORCEMENT_START, true ); // No enforcement start date set - set it now. if ( empty( $start_date ) ) { update_user_meta( $user_id, self::META_ENFORCEMENT_START, time() ); return true; } $days_elapsed = ( time() - ( is_numeric( $start_date ) ? (int) $start_date : 0 ) ) / DAY_IN_SECONDS; return $days_elapsed < $grace_days; } /** * Get remaining grace period days. * * @since 0.1.0 * * @param int $user_id User ID. * * @return int Remaining days in grace period. */ public function get_grace_period_remaining_days( int $user_id ): int { $settings = get_option( TWO_FACTOR_EXTENDED_OPTION_SETTINGS, array() ); if ( ! is_array( $settings ) ) { $settings = array(); } $grace_days = isset( $settings['grace_period_days'] ) && is_numeric( $settings['grace_period_days'] ) ? (int) $settings['grace_period_days'] : 0; $start_date = get_user_meta( $user_id, self::META_ENFORCEMENT_START, true ); if ( empty( $start_date ) || 0 === $grace_days ) { return 0; } $days_elapsed = ( time() - ( is_numeric( $start_date ) ? (int) $start_date : 0 ) ) / DAY_IN_SECONDS; $remaining = $grace_days - $days_elapsed; return max( 0, (int) ceil( $remaining ) ); } /** * Set grace period notice flag. * * @since 0.1.0 * * @param int $user_id User ID. */ private function set_grace_period_notice( int $user_id ): void { update_user_meta( $user_id, self::META_GRACE_NOTIFIED, time() ); } /** * Display user notices for 2FA requirements. * * @since 0.1.0 */ public function display_user_notices(): void { $user_id = get_current_user_id(); if ( ! $user_id ) { return; } $required_providers = $this->get_required_providers_for_user( $user_id ); if ( empty( $required_providers ) ) { return; } // Check if user meets requirements. if ( $this->user_meets_requirements( $user_id, $required_providers ) ) { return; } // Check if in grace period. if ( $this->is_in_grace_period( $user_id ) ) { $remaining_days = $this->get_grace_period_remaining_days( $user_id ); ?>

get_provider_labels( $required_providers ) ) ) ); ?>

get_provider_labels( $required_providers ) ) ) ); ?>

$provider_classes Array of provider class names. * * @return array Array of provider labels. */ private function get_provider_labels( array $provider_classes ): array { $names = Two_Factor_Extended_Provider_Detector::get_provider_names(); $labels = array(); foreach ( $provider_classes as $class ) { $labels[] = $names[ $class ] ?? $class; } return $labels; } /** * Check compliance for all users (scheduled task). * * @since 0.1.0 */ public function check_all_users_compliance(): void { // This will be implemented for reporting/audit purposes. // For now, it's a placeholder for the daily scheduled check. } }