init_hooks(); } /** * Initialize WordPress hooks. * * @since 0.1.0 */ private function init_hooks(): void { // Filter available providers for user. add_filter( 'two_factor_providers', array( $this, 'filter_providers_by_role' ), 10, 1 ); // Add explanation text in user profile. add_action( 'show_user_profile', array( $this, 'render_visibility_explanation' ), 5 ); add_action( 'edit_user_profile', array( $this, 'render_visibility_explanation' ), 5 ); } /** * Filter providers based on user role. * * @since 0.1.0 * * @param array $providers Available providers. * * @return array Filtered providers. */ public function filter_providers_by_role( array $providers ): array { // IMPORTANT: Only filter providers on user profile pages, NOT on admin settings pages. // Check if we're on a user profile/edit page. global $pagenow; $is_profile_page = ( 'profile.php' === $pagenow || 'user-edit.php' === $pagenow ); // Don't filter on our settings page or other admin pages. if ( ! $is_profile_page ) { return $providers; } // Get current user being edited. $user_id = $this->get_profile_user_id(); if ( ! $user_id ) { return $providers; } // Get visible providers for user. $visible_providers = $this->get_visible_providers_for_user( $user_id ); // If no visibility rules, show all providers. if ( null === $visible_providers ) { return $providers; } // Filter providers. $filtered = array(); foreach ( $providers as $class_name => $provider ) { // Always show required providers. if ( $this->is_required_provider( $user_id, $class_name ) ) { $filtered[ $class_name ] = $provider; continue; } // Show if visible for user's roles. if ( in_array( $class_name, $visible_providers, true ) ) { $filtered[ $class_name ] = $provider; } } return $filtered; } /** * Get visible providers for user based on their roles. * * @since 0.1.0 * * @param int $user_id User ID. * * @return array|null Array of visible provider class names, or null if no rules. */ public function get_visible_providers_for_user( int $user_id ): ?array { $settings = get_option( TWO_FACTOR_EXTENDED_OPTION_SETTINGS, array() ); $user_roles = Two_Factor_Extended_Role_Manager::get_user_roles( $user_id ); if ( empty( $user_roles ) || ! isset( $settings['provider_visibility'] ) ) { return null; } $visible = array(); // Collect visible providers from all user roles. foreach ( $user_roles as $role ) { if ( isset( $settings['provider_visibility'][ $role ] ) ) { $role_visible = $settings['provider_visibility'][ $role ]; if ( is_array( $role_visible ) ) { $visible = array_merge( $visible, $role_visible ); } } } // If no visibility rules for any role, return null (show all). if ( empty( $visible ) ) { return null; } return array_unique( $visible ); } /** * Check if provider is required for user. * * @since 0.1.0 * * @param int $user_id User ID. * @param string $provider_class Provider class name. * * @return bool True if provider is required. */ private function is_required_provider( int $user_id, string $provider_class ): bool { $enforcement = two_factor_extended()->get_enforcement(); if ( ! $enforcement ) { return false; } $required_providers = $enforcement->get_required_providers_for_user( $user_id ); return in_array( $provider_class, $required_providers, true ); } /** * Get user ID from profile page context. * * Uses filter_input() for safe GET parameter access when determining * which user's profile is being viewed. This is a read-only operation * used for displaying the correct 2FA provider options. * * @since 0.1.0 * * @return int|null User ID or null. */ private function get_profile_user_id(): ?int { // Check if editing another user's profile (e.g., wp-admin/user-edit.php?user_id=123). // Use filter_input() for safe access to GET parameter. $user_id = filter_input( INPUT_GET, 'user_id', FILTER_VALIDATE_INT ); if ( $user_id && $user_id > 0 ) { return $user_id; } // Check if editing own profile. return get_current_user_id(); } /** * Render visibility explanation in user profile. * * @since 0.1.0 * * @param WP_User $user User object. */ public function render_visibility_explanation( WP_User $user ): void { $visible_providers = $this->get_visible_providers_for_user( $user->ID ); // No visibility rules set. if ( null === $visible_providers ) { return; } $all_providers = Two_Factor_Extended_Provider_Detector::get_all_providers(); $required_providers = two_factor_extended()->get_enforcement()->get_required_providers_for_user( $user->ID ); $provider_names = Two_Factor_Extended_Provider_Detector::get_provider_names(); // Calculate hidden providers. $hidden_providers = array(); foreach ( array_keys( $all_providers ) as $class ) { // Skip required providers (always visible). if ( in_array( $class, $required_providers, true ) ) { continue; } // Not in visible list = hidden. if ( ! in_array( $class, $visible_providers, true ) ) { $hidden_providers[] = $provider_names[ $class ] ?? $class; } } if ( empty( $hidden_providers ) ) { return; } ?>

' . esc_html( implode( ', ', $hidden_providers ) ) . '' ); ?>

is_required_provider( $user_id, $provider_class ) ) { return true; } $visible_providers = $this->get_visible_providers_for_user( $user_id ); // No visibility rules = all visible. if ( null === $visible_providers ) { return true; } return in_array( $provider_class, $visible_providers, true ); } }