Changeset 3395451
- Timestamp:
- 11/14/2025 02:44:24 AM (5 months ago)
- Location:
- fuerte-wp/trunk
- Files:
-
- 10 edited
-
CHANGELOG.md (modified) (1 diff)
-
README.txt (modified) (1 diff)
-
SECURITY.md (modified) (1 diff)
-
admin/class-fuerte-wp-admin.php (modified) (8 diffs)
-
fuerte-wp.php (modified) (12 diffs)
-
includes/class-fuerte-wp-activator.php (modified) (3 diffs)
-
includes/class-fuerte-wp-config.php (modified) (5 diffs)
-
includes/class-fuerte-wp-enforcer.php (modified) (1 diff)
-
includes/class-fuerte-wp-login-logger.php (modified) (1 diff)
-
includes/class-fuerte-wp-login-manager.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
fuerte-wp/trunk/CHANGELOG.md
r3395378 r3395451 1 1 # Changelog 2 2 3 # 1.7. 1/ 2025-11-133 # 1.7.3 / 2025-11-13 4 4 - Added comprehensive Login Security system with rate limiting and IP lockout functionality. 5 5 - Implemented failed login attempt tracking with configurable thresholds and lockout durations. -
fuerte-wp/trunk/README.txt
r3395401 r3395451 2 2 Contributors: tcattd 3 3 Tags: security, login, protection, admin, brute-force, GDPR, privacy, access-control, multisite 4 Stable tag: 1.7. 24 Stable tag: 1.7.3 5 5 Requires at least: 6.0 6 6 Tested up to: 6.9 -
fuerte-wp/trunk/SECURITY.md
r3395378 r3395451 5 5 | Version | Supported | 6 6 | ------- | ------------------ | 7 | 1.7. 1| :white_check_mark: |8 | <1.7. 1| :x: |7 | 1.7.3 | :white_check_mark: | 8 | <1.7.3 | :x: | 9 9 10 10 ## Reporting a Vulnerability -
fuerte-wp/trunk/admin/class-fuerte-wp-admin.php
r3395378 r3395451 94 94 } 95 95 96 // Handle debug actions for troubleshooting cache issues 97 if (isset($_GET['fuertewp_action']) && current_user_can('manage_options')) { 98 switch ($_GET['fuertewp_action']) { 99 case 'debug_cache': 100 if (defined('WP_DEBUG') && WP_DEBUG) { 101 $debug_info = Fuerte_Wp_Config::debug_cache(); 102 echo '<div class="notice notice-info"><pre>'; 103 echo 'Fuerte-WP Cache Debug Information:' . "\n"; 104 echo '=====================================' . "\n"; 105 echo 'Cache Key: ' . $debug_info['cache_key'] . "\n"; 106 echo 'Cache Exists: ' . ($debug_info['cache_exists'] ? 'Yes' : 'No') . "\n"; 107 echo 'Cache Super Users: ' . print_r($debug_info['cache_super_users'], true) . "\n"; 108 echo 'Fresh Super Users: ' . print_r($debug_info['fresh_super_users'], true) . "\n"; 109 echo 'Cache Match: ' . ($debug_info['cache_match'] ? 'Yes' : 'No') . "\n"; 110 echo 'Current URL: ' . $debug_info['current_url'] . "\n"; 111 echo 'Current Screen: ' . $debug_info['current_screen'] . "\n"; 112 echo '</pre></div>'; 113 } 114 break; 115 116 case 'clear_cache': 117 Fuerte_Wp_Config::invalidate_cache(); 118 echo '<div class="notice notice-success"><p>Fuerte-WP configuration cache cleared.</p></div>'; 119 break; 120 121 case 'force_refresh': 122 $fresh_config = Fuerte_Wp_Config::force_refresh(); 123 echo '<div class="notice notice-success"><p>Fuerte-WP configuration refreshed from database.</p>'; 124 echo '<p>Super Users in database: ' . (isset($fresh_config['super_users']) ? print_r($fresh_config['super_users'], true) : 'Not set') . '</p></div>'; 125 break; 126 } 127 } 128 96 129 /* 97 130 * Allow admin options for super users even if config file exists … … 623 656 function updateLoginUrlPreview() { 624 657 var enabled = $(\'input[name="fuertewp_login_url_hiding_enabled"]\').prop(\'checked\'); 625 var slug = $(\'input[name=" fuertewp_custom_login_slug"]\').val() || \'secure-login\';658 var slug = $(\'input[name="_fuertewp_custom_login_slug"]\').val() || \'secure-login\'; 626 659 var urlType = $(\'select[name="fuertewp_login_url_type"]\').val() || \'query_param\'; 627 660 var baseUrl = window.location.origin + window.location.pathname.replace(/\/wp-admin.*$/, \'/\'); … … 645 678 $(\'#carbon_fields_container\').on(\'submit\', function() { 646 679 var loginHidingEnabled = $(\'input[name="fuertewp_login_url_hiding_enabled"]\').prop(\'checked\'); 647 var customSlug = $(\'input[name=" fuertewp_custom_login_slug"]\').val();680 var customSlug = $(\'input[name="_fuertewp_custom_login_slug"]\').val(); 648 681 649 682 if (loginHidingEnabled && (customSlug === \'\' || customSlug.trim() === \'\')) { 650 683 // Set default value before form submission 651 $(\'input[name=" fuertewp_custom_login_slug"]\').val(\'secure-login\');684 $(\'input[name="_fuertewp_custom_login_slug"]\').val(\'secure-login\'); 652 685 653 686 // Update preview to show the new default … … 659 692 $(\'input[name="fuertewp_login_url_hiding_enabled"]\').on(\'change\', function() { 660 693 var enabled = $(this).prop(\'checked\'); 661 var customSlug = $(\'input[name=" fuertewp_custom_login_slug"]\').val();694 var customSlug = $(\'input[name="_fuertewp_custom_login_slug"]\').val(); 662 695 663 696 if (enabled && (customSlug === \'\' || customSlug.trim() === \'\')) { 664 $(\'input[name=" fuertewp_custom_login_slug"]\').val(\'secure-login\');697 $(\'input[name="_fuertewp_custom_login_slug"]\').val(\'secure-login\'); 665 698 updateLoginUrlPreview(); 666 699 } … … 669 702 // Update preview when settings change 670 703 $(\'input[name="fuertewp_login_url_hiding_enabled"]\').on(\'change\', updateLoginUrlPreview); 671 $(\'input[name=" fuertewp_custom_login_slug"]\').on(\'input\', updateLoginUrlPreview);704 $(\'input[name="_fuertewp_custom_login_slug"]\').on(\'input\', updateLoginUrlPreview); 672 705 $(\'select[name="fuertewp_login_url_type"]\').on(\'change\', updateLoginUrlPreview); 673 706 … … 1226 1259 1227 1260 // Validate and ensure custom login slug is never empty 1228 $custom_login_slug = carbon_get_theme_option('fuertewp_custom_login_slug');1261 $custom_login_slug = Fuerte_Wp_Config::get_field('custom_login_slug', 'secure-login'); 1229 1262 if (empty($custom_login_slug) || trim($custom_login_slug) === '') { 1230 carbon_set_theme_option('fuertewp_custom_login_slug', 'secure-login');1263 Fuerte_Wp_Config::set_field('custom_login_slug', 'secure-login'); 1231 1264 } 1232 1265 … … 1236 1269 } 1237 1270 1238 $super_users = carbon_get_theme_option('fuertewp_super_users');1271 $super_users = Fuerte_Wp_Config::get_field('super_users', [], true); 1239 1272 1240 1273 // Normalize to array format for consistency … … 1254 1287 array_unshift($super_users, $current_user->user_email); 1255 1288 1256 carbon_set_theme_option('fuertewp_super_users', $super_users);1289 Fuerte_Wp_Config::set_field('super_users', $super_users, true); 1257 1290 } 1258 1291 } 1259 1292 1260 // Set default login security values using direct options1293 // Set default login security values using standardized _fuertewp_ pattern 1261 1294 $defaults_to_set = [ 1262 ' fuertewp_login_enable' => 'enabled',1263 ' fuertewp_registration_enable' => 'enabled',1264 ' fuertewp_login_max_attempts' => 5,1265 ' fuertewp_login_lockout_duration' => 15,1266 ' fuertewp_custom_login_slug' => 'secure-login', // Ensure default login slug is always set1295 '_fuertewp_login_enable' => 'enabled', 1296 '_fuertewp_registration_enable' => 'enabled', 1297 '_fuertewp_login_max_attempts' => 5, 1298 '_fuertewp_login_lockout_duration' => 15, 1299 '_fuertewp_custom_login_slug' => 'secure-login', // Ensure default login slug is always set 1267 1300 ]; 1268 1301 -
fuerte-wp/trunk/fuerte-wp.php
r3395401 r3395451 6 6 * Plugin URI: https://github.com/EstebanForge/Fuerte-WP 7 7 * Description: Stronger WP. Limit access to critical WordPress areas, even other for admins. 8 * Version: 1.7. 28 * Version: 1.7.3 9 9 * Author: Esteban Cuevas 10 10 * Author URI: https://actitud.xyz … … 13 13 * Text Domain: fuerte-wp 14 14 * Domain Path: /languages 15 * Requires at least: 6. 015 * Requires at least: 6.4 16 16 * Tested up to: 6.9 17 17 * Requires PHP: 8.1 … … 19 19 * @link https://actitud.xyz 20 20 * @since 1.3.0 21 * @package Fuerte_Wp22 21 */ 23 22 24 23 // If this file is called directly, abort. 25 if (!defined( "WPINC")) {26 die();27 } 28 29 /* *24 if (!defined('WPINC')) { 25 die(); 26 } 27 28 /* 30 29 * Currently plugin version. 31 30 * Start at version 1.0.0 and use SemVer - https://semver.org 32 31 * Rename this for your plugin and update it as you release new versions. 33 32 */ 34 define( "FUERTEWP_PLUGIN_BASE", plugin_basename(__FILE__));35 define( "FUERTEWP_VERSION", "1.7.2");36 define( "FUERTEWP_PATH", realpath(plugin_dir_path(__FILE__)) . "/");37 define( "FUERTEWP_URL", trailingslashit(plugin_dir_url(__FILE__)));38 define( "FUERTEWP_LATE_PRIORITY", 9999);39 40 /* *33 define('FUERTEWP_PLUGIN_BASE', plugin_basename(__FILE__)); 34 define('FUERTEWP_VERSION', '1.7.3'); 35 define('FUERTEWP_PATH', realpath(plugin_dir_path(__FILE__)) . '/'); 36 define('FUERTEWP_URL', trailingslashit(plugin_dir_url(__FILE__))); 37 define('FUERTEWP_LATE_PRIORITY', 9999); 38 39 /* 41 40 * Load Fuerte-WP config file if exist 42 41 */ 43 if (file_exists(ABSPATH . "wp-config-fuerte.php")) {44 require_once ABSPATH . "wp-config-fuerte.php";45 } 46 47 /* *42 if (file_exists(ABSPATH . 'wp-config-fuerte.php')) { 43 require_once ABSPATH . 'wp-config-fuerte.php'; 44 } 45 46 /* 48 47 * Exit if FUERTEWP_DISABLE is defined (in wp-config.php or wp-config-fuerte.php) 49 48 */ 50 if (defined( "FUERTEWP_DISABLE") && true === FUERTEWP_DISABLE) {51 return false;52 } 53 54 /** 55 * Includes & Autoload 49 if (defined('FUERTEWP_DISABLE') && true === FUERTEWP_DISABLE) { 50 return false; 51 } 52 53 /** 54 * Includes & Autoload. 56 55 */ 57 56 function fuertewp_includes_autoload() 58 57 { 59 if (file_exists(FUERTEWP_PATH . "includes/helpers.php")) {60 require_once FUERTEWP_PATH . "includes/helpers.php";61 }62 63 // Elementor has JS issues with Carbon-Fields being loaded while in his editor.64 if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "elementor") {65 return;66 }67 68 if (file_exists(FUERTEWP_PATH . "vendor/autoload.php")) {69 require_once FUERTEWP_PATH . "vendor/autoload.php";70 }71 } 72 add_action( "after_setup_theme", "fuertewp_includes_autoload", 100);58 if (file_exists(FUERTEWP_PATH . 'includes/helpers.php')) { 59 require_once FUERTEWP_PATH . 'includes/helpers.php'; 60 } 61 62 // Elementor has JS issues with Carbon-Fields being loaded while in his editor. 63 if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'elementor') { 64 return; 65 } 66 67 if (file_exists(FUERTEWP_PATH . 'vendor/autoload.php')) { 68 require_once FUERTEWP_PATH . 'vendor/autoload.php'; 69 } 70 } 71 add_action('after_setup_theme', 'fuertewp_includes_autoload', 100); 73 72 74 73 /** … … 80 79 function fuertewp_load_carbon_fields() 81 80 { 82 if (file_exists(FUERTEWP_PATH . "vendor/autoload.php")) {81 if (file_exists(FUERTEWP_PATH . 'vendor/autoload.php')) { 83 82 // Carbon Fields configuration 84 83 // https://github.com/htmlburger/carbon-fields/issues/805#issuecomment-680959592 85 84 // https://docs.carbonfields.net/learn/advanced-topics/compacting-input-vars.html 86 85 define( 87 "Carbon_Fields\\URL",88 FUERTEWP_URL . "vendor/htmlburger/carbon-fields/",86 'Carbon_Fields\\URL', 87 FUERTEWP_URL . 'vendor/htmlburger/carbon-fields/', 89 88 ); 90 define( "Carbon_Fields\\COMPACT_INPUT", true);91 define( "Carbon_Fields\\COMPACT_INPUT_KEY", "fuertewp_carbonfields");92 93 require_once FUERTEWP_PATH . "vendor/autoload.php";89 define('Carbon_Fields\\COMPACT_INPUT', true); 90 define('Carbon_Fields\\COMPACT_INPUT_KEY', 'fuertewp_carbonfields'); 91 92 require_once FUERTEWP_PATH . 'vendor/autoload.php'; 94 93 95 94 // Initialize Carbon Fields 96 \Carbon_Fields\Carbon_Fields::boot();95 Carbon_Fields\Carbon_Fields::boot(); 97 96 } 98 97 } … … 101 100 /** 102 101 * The code that runs during plugin activation. 103 * This action is documented in includes/class-fuerte-wp-activator.php 102 * This action is documented in includes/class-fuerte-wp-activator.php. 104 103 */ 105 104 function activate_fuerte_wp() 106 105 { 107 require_once plugin_dir_path(__FILE__) . 108 "includes/class-fuerte-wp-activator.php";109 Fuerte_Wp_Activator::activate();106 require_once plugin_dir_path(__FILE__) 107 . 'includes/class-fuerte-wp-activator.php'; 108 Fuerte_Wp_Activator::activate(); 110 109 } 111 110 112 111 /** 113 112 * The code that runs during plugin deactivation. 114 * This action is documented in includes/class-fuerte-wp-deactivator.php 113 * This action is documented in includes/class-fuerte-wp-deactivator.php. 115 114 */ 116 115 function deactivate_fuerte_wp() 117 116 { 118 require_once plugin_dir_path(__FILE__) . 119 "includes/class-fuerte-wp-deactivator.php";120 Fuerte_Wp_Deactivator::deactivate();121 } 122 123 register_activation_hook(__FILE__, "activate_fuerte_wp");124 register_deactivation_hook(__FILE__, "deactivate_fuerte_wp");117 require_once plugin_dir_path(__FILE__) 118 . 'includes/class-fuerte-wp-deactivator.php'; 119 Fuerte_Wp_Deactivator::deactivate(); 120 } 121 122 register_activation_hook(__FILE__, 'activate_fuerte_wp'); 123 register_deactivation_hook(__FILE__, 'deactivate_fuerte_wp'); 125 124 126 125 /** … … 132 131 function fuertewp_check_login_db_version() 133 132 { 134 require_once plugin_dir_path(__FILE__) . "includes/class-fuerte-wp-activator.php";135 136 $installed_version = get_option(' fuertewp_login_db_version', '0.0.0');137 138 if (version_compare($installed_version, Fuerte_Wp_Activator:: DB_VERSION, '<')) {133 require_once plugin_dir_path(__FILE__) . 'includes/class-fuerte-wp-activator.php'; 134 135 $installed_version = get_option('_fuertewp_login_db_version', '0.0.0'); 136 137 if (version_compare($installed_version, Fuerte_Wp_Activator::get_db_version(), '<')) { 139 138 Fuerte_Wp_Activator::create_login_security_tables(); 140 139 Fuerte_Wp_Activator::schedule_cron_jobs(); … … 176 175 177 176 /** 178 * Code that runs on plugins uninstallation 177 * Code that runs on plugins uninstallation. 179 178 */ 180 179 function uninstall_fuerte_wp() 181 180 { 182 require_once plugin_dir_path(__FILE__) . 183 "includes/class-fuerte-wp-uninstaller.php";184 Fuerte_Wp_Uninstaller::uninstall();181 require_once plugin_dir_path(__FILE__) 182 . 'includes/class-fuerte-wp-uninstaller.php'; 183 Fuerte_Wp_Uninstaller::uninstall(); 185 184 } 186 185 … … 190 189 */ 191 190 // Load logger first to ensure it's always available for debugging 192 require plugin_dir_path(__FILE__) . "includes/class-fuerte-wp-logger.php";191 require plugin_dir_path(__FILE__) . 'includes/class-fuerte-wp-logger.php'; 193 192 194 193 // Initialize logger immediately - only enable when WP_DEBUG is true … … 196 195 Fuerte_Wp_Logger::init_from_constant(); 197 196 198 require plugin_dir_path(__FILE__) . "includes/class-fuerte-wp.php";197 require plugin_dir_path(__FILE__) . 'includes/class-fuerte-wp.php'; 199 198 200 199 /** … … 209 208 function run_fuerte_wp() 210 209 { 211 $plugin = new Fuerte_Wp();212 $plugin->run();210 $plugin = new Fuerte_Wp(); 211 $plugin->run(); 213 212 } 214 213 run_fuerte_wp(); 215 214 216 /* *215 /* 217 216 * Hook into plugin updates to clear configuration cache. 218 217 * This ensures that the super users and other configuration are properly … … 223 222 add_action('upgrader_process_complete', 'fuertewp_handle_plugin_update', 10, 2); 224 223 225 function fuertewp_handle_plugin_update($upgrader_object, $options) { 224 function fuertewp_handle_plugin_update($upgrader_object, $options) 225 { 226 226 // Check if this is a plugin update and if it's our plugin 227 227 if ($options['action'] === 'update' && $options['type'] === 'plugin') { … … 240 240 241 241 /** 242 * htaccess security rules 242 * htaccess security rules. 243 243 */ 244 244 $fuertewp_htaccess = " -
fuerte-wp/trunk/includes/class-fuerte-wp-activator.php
r3395401 r3395451 26 26 /** 27 27 * Database version for login security feature. 28 * Increment when database schema changes. 29 * 30 * @since 1.7.0 31 */ 32 const DB_VERSION = '1.0.0'; 28 * Uses plugin version to ensure consistency. 29 * 30 * @since 1.7.3 31 */ 32 public static function get_db_version() 33 { 34 return defined('FUERTEWP_VERSION') ? FUERTEWP_VERSION : '1.0.0'; 35 } 33 36 34 37 /** … … 52 55 * Set up initial super user from current admin user. 53 56 * This prevents lockout when the plugin is first activated. 54 * 55 * @since 1.7.0 57 * Uses standardized Config class methods to avoid Carbon Fields timing issues. 58 * 59 * @since 1.7.2 56 60 */ 57 61 public static function setup_initial_super_user() 58 62 { 59 // Check if Carbon Fields functions are available 60 // During activation, Carbon Fields may not be loaded yet 61 if (!function_exists('carbon_get_theme_option')) { 63 // Use our standardized Config class methods instead of Carbon Fields 64 if (!class_exists('Fuerte_Wp_Config')) { 62 65 return; 63 66 } 64 67 65 68 // Check if super_users is already set 66 $existing_super_users = carbon_get_theme_option('fuertewp_super_users');67 68 if (empty($existing_super_users) || !is_array($existing_super_users)) {69 $existing_super_users = Fuerte_Wp_Config::get_field('super_users', [], true); 70 71 if (empty($existing_super_users)) { 69 72 // Get current user if available (works during activation) 70 73 $current_user = wp_get_current_user(); 71 74 72 75 if ($current_user && $current_user->ID > 0) { 73 // Add current user as super user 74 carbon_set_theme_option('fuertewp_super_users', [$current_user->user_email]);76 // Add current user as super user using our standardized method 77 Fuerte_Wp_Config::set_field('super_users', [$current_user->user_email], true); 75 78 } 76 79 } … … 144 147 145 148 // Store database version 146 add_option(' fuertewp_login_db_version', self::DB_VERSION);149 add_option('_fuertewp_login_db_version', self::get_db_version()); 147 150 } 148 151 -
fuerte-wp/trunk/includes/class-fuerte-wp-config.php
r3395361 r3395451 73 73 * 74 74 * @since 1.7.0 75 * @param bool $bypass_cache Force loading from database, bypassing transient cache 75 76 * @return array Configuration array 76 77 */ 77 public static function get_config() 78 { 79 // Try to get from transient first 80 $cached_config = get_transient(self::$transient_key); 81 if ($cached_config !== false) { 82 return $cached_config; 78 public static function get_config($bypass_cache = false) 79 { 80 // Bypass cache if requested or if on settings page for admin users 81 $force_bypass = $bypass_cache || self::should_bypass_cache(); 82 83 // Try to get from transient first (unless bypassing) 84 if (!$force_bypass) { 85 $cached_config = get_transient(self::$transient_key); 86 if ($cached_config !== false) { 87 return $cached_config; 88 } 83 89 } 84 90 … … 90 96 } 91 97 92 self::save_config($config); 98 // Only save to cache if not bypassing 99 if (!$force_bypass) { 100 self::save_config($config); 101 } 93 102 94 103 return $config; … … 150 159 151 160 /** 161 * Force configuration refresh from database. 162 * Useful for debugging cache issues. 163 * 164 * @since 1.7.2 165 * @return array Fresh configuration from database 166 */ 167 public static function force_refresh() 168 { 169 self::invalidate_cache(); 170 return self::get_config(true); 171 } 172 173 /** 174 * Debug method to check what's in the cache vs database. 175 * Only works for admin users and when WP_DEBUG is enabled. 176 * 177 * @since 1.7.2 178 * @return array Debug information 179 */ 180 public static function debug_cache() 181 { 182 if (!current_user_can('manage_options') || !defined('WP_DEBUG') || !WP_DEBUG) { 183 return []; 184 } 185 186 $cached = get_transient(self::$transient_key); 187 $fresh = self::get_config(true); 188 189 return [ 190 'cache_key' => self::$transient_key, 191 'cache_exists' => $cached !== false, 192 'cache_super_users' => isset($cached['super_users']) ? $cached['super_users'] : 'not_set', 193 'fresh_super_users' => isset($fresh['super_users']) ? $fresh['super_users'] : 'not_set', 194 'cache_match' => json_encode($cached) === json_encode($fresh), 195 'current_url' => $_SERVER['REQUEST_URI'] ?? 'unknown', 196 'current_screen' => (function_exists('get_current_screen') && get_current_screen()) ? get_current_screen()->id : 'function_not_available_or_no_screen', 197 ]; 198 } 199 200 /** 152 201 * Load configuration from database. 153 202 * … … 159 208 $config = []; 160 209 161 // Load from individual options stored by Carbon Fields 162 $config['status'] = get_option('fuertewp_status', 'enabled'); 163 164 // Load super users from Carbon Fields 165 $super_users = get_option('_fuertewp_super_users', ''); 166 $config['super_users'] = !empty($super_users) ? array($super_users) : array(); 210 // Load status from _fuertewp_status option 211 $config['status'] = get_option('_fuertewp_status', 'enabled'); 212 213 // Load super users with special handling for multiselect field 214 $config['super_users'] = self::load_multiselect_field('fuertewp_super_users'); 167 215 168 216 // Load general settings … … 236 284 return $config; 237 285 } 286 287 /** 288 * Check if cache should be bypassed for the current request. 289 * Bypasses cache for admin users on settings pages to ensure fresh data. 290 * 291 * @since 1.7.2 292 * @return bool True if cache should be bypassed 293 */ 294 private static function should_bypass_cache() 295 { 296 // Only bypass for admin requests 297 if (!is_admin()) { 298 return false; 299 } 300 301 // Check if current user can manage options (admin level) 302 if (!current_user_can('manage_options')) { 303 return false; 304 } 305 306 // Bypass for any page with fuerte-wp in the URL 307 if (isset($_GET['page']) && strpos($_GET['page'], 'fuerte-wp') !== false) { 308 return true; 309 } 310 311 // Only check screen if we're in admin area and function exists 312 if (function_exists('get_current_screen')) { 313 $current_screen = get_current_screen(); 314 if ($current_screen) { 315 // Bypass for Fuerte-WP settings pages 316 if ($current_screen->id === 'settings_page_fuerte-wp-options' || 317 $current_screen->id === 'toplevel_page_fuerte-wp-options') { 318 return true; 319 } 320 } 321 } 322 323 return false; 324 } 325 326 /** 327 * Standardized method to get any configuration value with proper fallback logic. 328 * Handles both direct options and Carbon Fields multiselect patterns automatically. 329 * 330 * @since 1.7.2 331 * @param string $key Configuration key (without _fuertewp prefix) 332 * @param mixed $default Default value if option doesn't exist 333 * @param bool $is_multiselect Whether this field uses Carbon Fields multiselect pattern 334 * @return mixed Configuration value 335 */ 336 public static function get_field($key, $default = null, $is_multiselect = false) 337 { 338 $option_name = "_fuertewp_{$key}"; 339 340 if ($is_multiselect) { 341 return self::load_multiselect_field("fuertewp_{$key}"); 342 } 343 344 return get_option($option_name, $default); 345 } 346 347 /** 348 * Standardized method to set any configuration value. 349 * 350 * @since 1.7.2 351 * @param string $key Configuration key (without _fuertewp prefix) 352 * @param mixed $value Value to set 353 * @param bool $is_multiselect Whether this field uses Carbon Fields multiselect pattern 354 * @return bool Success status 355 */ 356 public static function set_field($key, $value, $is_multiselect = false) 357 { 358 $option_name = "_fuertewp_{$key}"; 359 360 if ($is_multiselect && is_array($value)) { 361 return self::save_multiselect_field("fuertewp_{$key}", $value); 362 } 363 364 return update_option($option_name, $value); 365 } 366 367 /** 368 * Save multiselect field values using Carbon Fields compatible pattern. 369 * 370 * @since 1.7.2 371 * @param string $field_name Base field name without prefix 372 * @param array $values Array of values to save 373 * @return bool Success status 374 */ 375 public static function save_multiselect_field($field_name, $values) 376 { 377 global $wpdb; 378 379 // First, delete all existing entries for this field 380 $option_name_prefix = "_{$field_name}|||"; 381 $wpdb->query($wpdb->prepare( 382 "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", 383 $option_name_prefix . '%' 384 )); 385 386 // Now insert the new values 387 $success = true; 388 foreach ($values as $index => $value) { 389 $option_name = "_{$field_name}|||{$index}|value"; 390 $result = update_option($option_name, $value); 391 if (!$result) { 392 $success = false; 393 } 394 } 395 396 return $success; 397 } 398 399 /** 400 * Clean up duplicate storage patterns from database. 401 * Removes old Carbon Fields storage patterns while preserving _fuertewp_* data. 402 * 403 * @since 1.7.2 404 * @return array Cleanup results 405 */ 406 public static function cleanup_duplicate_patterns() 407 { 408 global $wpdb; 409 410 $cleanup_results = array( 411 'deleted_fuertewp_options' => 0, 412 'deleted_carbon_fields_options' => 0, 413 'preserved_fuertewp_options' => 0 414 ); 415 416 // Only proceed if user has admin capabilities 417 if (!current_user_can('manage_options')) { 418 return $cleanup_results; 419 } 420 421 // Get all fuerte-related options 422 $fuerte_options = $wpdb->get_results( 423 "SELECT option_name, option_value 424 FROM {$wpdb->options} 425 WHERE option_name LIKE '%fuerte%' 426 ORDER BY option_name ASC" 427 ); 428 429 foreach ($fuerte_options as $option) { 430 $option_name = $option->option_name; 431 432 // Delete old fuertewp_* options (without underscore prefix) 433 if (strpos($option_name, 'fuertewp_') === 0 && strpos($option_name, '_fuertewp_') !== 0) { 434 // Special handling for login_db_version migration 435 if ($option_name === 'fuertewp_login_db_version') { 436 $underscored_name = '_fuertewp_login_db_version'; 437 if (!get_option($underscored_name)) { 438 $current_version = defined('FUERTEWP_VERSION') ? FUERTEWP_VERSION : '1.7.3'; 439 update_option($underscored_name, $current_version); // Set to current plugin version 440 $cleanup_results['preserved_fuertewp_options']++; 441 } 442 } else { 443 // First, migrate data to _fuertewp_ version if it doesn't exist 444 $underscored_name = '_' . $option_name; 445 if (!get_option($underscored_name)) { 446 update_option($underscored_name, $option->option_value); 447 $cleanup_results['preserved_fuertewp_options']++; 448 } 449 } 450 451 // Delete the old option 452 delete_option($option_name); 453 $cleanup_results['deleted_fuertewp_options']++; 454 continue; 455 } 456 457 // Delete Carbon Fields compacted options 458 if (strpos($option_name, '_carbon_fields_theme_options_fuerte-wp') === 0) { 459 delete_option($option_name); 460 $cleanup_results['deleted_carbon_fields_options']++; 461 continue; 462 } 463 464 // Delete double underscore options created by Carbon Fields field definitions 465 if (strpos($option_name, '__fuertewp_') === 0) { 466 // First, migrate data to single underscore version if it doesn't exist 467 $single_underscore_name = '_' . substr($option_name, 2); // Remove one underscore 468 if (!get_option($single_underscore_name)) { 469 update_option($single_underscore_name, $option->option_value); 470 $cleanup_results['preserved_fuertewp_options']++; 471 } 472 473 // Delete the double underscore option 474 delete_option($option_name); 475 $cleanup_results['deleted_fuertewp_options']++; 476 continue; 477 } 478 } 479 480 // Clear the cache after cleanup 481 self::invalidate_cache(); 482 483 return $cleanup_results; 484 } 485 486 /** 487 * Get configuration batch for enforcer class using standardized methods. 488 * Replaces all direct get_option/carbon_get_theme_option calls. 489 * 490 * @since 1.7.2 491 * @return array Configuration options array 492 */ 493 public static function get_enforcer_config() 494 { 495 return array( 496 // Status and core settings 497 'fuertewp_status' => self::get_field('status', 'enabled'), 498 'fuertewp_super_users' => self::get_field('super_users', [], true), 499 500 // General settings 501 'fuertewp_access_denied_message' => self::get_field('access_denied_message', 'Access denied.'), 502 'fuertewp_recovery_email' => self::get_field('recovery_email', ''), 503 'fuertewp_sender_email_enable' => self::get_field('sender_email_enable', true), 504 'fuertewp_sender_email' => self::get_field('sender_email', ''), 505 'fuertewp_autoupdate_core' => self::get_field('autoupdate_core', false), 506 'fuertewp_autoupdate_plugins' => self::get_field('autoupdate_plugins', false), 507 'fuertewp_autoupdate_themes' => self::get_field('autoupdate_themes', false), 508 'fuertewp_autoupdate_translations' => self::get_field('autoupdate_translations', false), 509 'fuertewp_autoupdate_frequency' => self::get_field('autoupdate_frequency', 'daily'), 510 511 // Login security settings 512 'fuertewp_login_enable' => self::get_field('login_enable', 'enabled'), 513 'fuertewp_registration_enable' => self::get_field('registration_enable', 'enabled'), 514 'fuertewp_login_max_attempts' => self::get_field('login_max_attempts', 5), 515 'fuertewp_login_lockout_duration' => self::get_field('login_lockout_duration', 60), 516 'fuertewp_login_increasing_lockout' => self::get_field('login_increasing_lockout', false), 517 'fuertewp_login_ip_headers' => self::get_field('login_ip_headers', ''), 518 'fuertewp_login_gdpr_message' => self::get_field('login_gdpr_message', ''), 519 'fuertewp_login_data_retention' => self::get_field('login_data_retention', 30), 520 'fuertewp_login_url_hiding_enabled' => self::get_field('login_url_hiding_enabled', false), 521 'fuertewp_custom_login_slug' => self::get_field('custom_login_slug', 'secure-login'), 522 'fuertewp_login_url_type' => self::get_field('login_url_type', 'query_param'), 523 'fuertewp_redirect_invalid_logins' => self::get_field('redirect_invalid_logins', 'home_404'), 524 'fuertewp_redirect_invalid_logins_url' => self::get_field('redirect_invalid_logins_url', ''), 525 526 // Email settings 527 'fuertewp_emails_fatal_error' => self::get_field('emails_fatal_error', true), 528 'fuertewp_emails_automatic_updates' => self::get_field('emails_automatic_updates', false), 529 'fuertewp_emails_comment_awaiting_moderation' => self::get_field('emails_comment_awaiting_moderation', false), 530 'fuertewp_emails_comment_has_been_published' => self::get_field('emails_comment_has_been_published', false), 531 'fuertewp_emails_user_reset_their_password' => self::get_field('emails_user_reset_their_password', false), 532 'fuertewp_emails_user_confirm_personal_data_export_request' => self::get_field('emails_user_confirm_personal_data_export_request', false), 533 'fuertewp_emails_new_user_created' => self::get_field('emails_new_user_created', true), 534 'fuertewp_emails_network_new_site_created' => self::get_field('emails_network_new_site_created', false), 535 'fuertewp_emails_network_new_user_site_registered' => self::get_field('emails_network_new_user_site_registered', false), 536 'fuertewp_emails_network_new_site_activated' => self::get_field('emails_network_new_site_activated', false), 537 538 // Restrictions 539 'fuertewp_restrictions_restapi_loggedin_only' => self::get_field('restrictions_restapi_loggedin_only', false), 540 'fuertewp_restrictions_restapi_disable_app_passwords' => self::get_field('restrictions_restapi_disable_app_passwords', true), 541 'fuertewp_restrictions_disable_xmlrpc' => self::get_field('restrictions_disable_xmlrpc', true), 542 'fuertewp_restrictions_htaccess_security_rules' => self::get_field('restrictions_htaccess_security_rules', true), 543 'fuertewp_restrictions_disable_admin_create_edit' => self::get_field('restrictions_disable_admin_create_edit', true), 544 'fuertewp_restrictions_disable_weak_passwords' => self::get_field('restrictions_disable_weak_passwords', true), 545 'fuertewp_restrictions_force_strong_passwords' => self::get_field('restrictions_force_strong_passwords', false), 546 'fuertewp_restrictions_disable_admin_bar_roles' => self::get_field('restrictions_disable_admin_bar_roles', ['subscriber', 'customer'], true), 547 'fuertewp_restrictions_restrict_permalinks' => self::get_field('restrictions_restrict_permalinks', true), 548 'fuertewp_restrictions_restrict_acf' => self::get_field('restrictions_restrict_acf', true), 549 'fuertewp_restrictions_disable_theme_editor' => self::get_field('restrictions_disable_theme_editor', true), 550 'fuertewp_restrictions_disable_plugin_editor' => self::get_field('restrictions_disable_plugin_editor', true), 551 'fuertewp_restrictions_disable_theme_install' => self::get_field('restrictions_disable_theme_install', true), 552 'fuertewp_restrictions_disable_plugin_install' => self::get_field('restrictions_disable_plugin_install', true), 553 'fuertewp_restrictions_disable_customizer_css' => self::get_field('restrictions_disable_customizer_css', true), 554 555 // Advanced restrictions (arrays) 556 'fuertewp_restricted_scripts' => self::get_field('restricted_scripts', [], true), 557 'fuertewp_restricted_pages' => self::get_field('restricted_pages', [], true), 558 'fuertewp_removed_menus' => self::get_field('removed_menus', [], true), 559 'fuertewp_removed_submenus' => self::get_field('removed_submenus', [], true), 560 'fuertewp_removed_adminbar_menus' => self::get_field('removed_adminbar_menus', [], true), 561 ); 562 } 563 564 /** 565 * Load multiselect field values from database. 566 * Handles the Carbon Fields _field_name|||index|value pattern. 567 * 568 * @since 1.7.2 569 * @param string $field_name Base field name without prefix 570 * @return array Array of field values 571 */ 572 private static function load_multiselect_field($field_name) 573 { 574 global $wpdb; 575 576 $option_name_prefix = "_{$field_name}|||"; 577 $values = array(); 578 579 // Get all options matching the multiselect pattern 580 $results = $wpdb->get_results($wpdb->prepare( 581 "SELECT option_name, option_value 582 FROM {$wpdb->options} 583 WHERE option_name LIKE %s 584 ORDER BY option_name ASC", 585 $option_name_prefix . '%' 586 )); 587 588 foreach ($results as $result) { 589 // Parse the option name to get the index 590 // Pattern: _fuertewp_super_users|||0|value 591 if (preg_match('/\|\|\|(\d+)\|value$/', $result->option_name, $matches)) { 592 $values[intval($matches[1])] = $result->option_value; 593 } 594 } 595 596 // Sort by index and re-index array 597 ksort($values); 598 return array_values($values); 599 } 238 600 } -
fuerte-wp/trunk/includes/class-fuerte-wp-enforcer.php
r3395401 r3395451 544 544 545 545 /** 546 * Get configuration options using Carbon Fields API. 547 * This ensures proper integration with Carbon Fields custom datastore. 546 * Get configuration options using standardized Config class methods. 547 * Replaces all direct get_option/carbon_get_theme_option calls with a single standardized method. 548 * 549 * @since 1.7.2 550 * @return array Configuration options 548 551 */ 549 552 private function get_config_options_batch() 550 553 { 551 // Use Carbon Fields API to get theme options 552 // This handles the custom datastore and underscore prefix correctly 553 $options = []; 554 555 // Only attempt to get Carbon Fields options if the function exists 556 if (function_exists('carbon_get_theme_option')) { 557 $options['fuertewp_status'] = carbon_get_theme_option('fuertewp_status'); 558 // Get super users from Carbon Fields (where self-healing stores them) 559 $super_users = carbon_get_theme_option('fuertewp_super_users'); 560 561 // Normalize to array format for consistency 562 if (is_string($super_users) && !empty($super_users)) { 563 $options['super_users'] = [$super_users]; 564 } elseif (is_array($super_users)) { 565 $options['super_users'] = $super_users; 566 } else { 567 $options['super_users'] = []; 568 } 569 $options['fuertewp_access_denied_message'] = carbon_get_theme_option('fuertewp_access_denied_message'); 570 $options['fuertewp_recovery_email'] = carbon_get_theme_option('fuertewp_recovery_email'); 571 $options['fuertewp_sender_email_enable'] = carbon_get_theme_option('fuertewp_sender_email_enable'); 572 $options['fuertewp_sender_email'] = carbon_get_theme_option('fuertewp_sender_email'); 573 $options['fuertewp_autoupdate_core'] = carbon_get_theme_option('fuertewp_autoupdate_core'); 574 $options['fuertewp_autoupdate_plugins'] = carbon_get_theme_option('fuertewp_autoupdate_plugins'); 575 $options['fuertewp_autoupdate_themes'] = carbon_get_theme_option('fuertewp_autoupdate_themes'); 576 $options['fuertewp_autoupdate_translations'] = carbon_get_theme_option('fuertewp_autoupdate_translations'); 577 $options['fuertewp_autoupdate_frequency'] = carbon_get_theme_option('fuertewp_autoupdate_frequency'); 578 579 // Login Security settings 580 $options['fuertewp_login_enable'] = carbon_get_theme_option('fuertewp_login_enable'); 581 $options['fuertewp_registration_enable'] = carbon_get_theme_option('fuertewp_registration_enable'); 582 $options['fuertewp_login_max_attempts'] = carbon_get_theme_option('fuertewp_login_max_attempts'); 583 $options['fuertewp_login_lockout_duration'] = carbon_get_theme_option('fuertewp_login_lockout_duration'); 584 $options['fuertewp_login_lockout_duration_type'] = carbon_get_theme_option('fuertewp_login_lockout_duration_type'); 585 $options['fuertewp_login_cron_cleanup_frequency'] = carbon_get_theme_option('fuertewp_login_cron_cleanup_frequency'); 586 587 $options['fuertewp_tweaks_use_site_logo_login'] = carbon_get_theme_option('fuertewp_tweaks_use_site_logo_login'); 588 $options['fuertewp_emails_fatal_error'] = carbon_get_theme_option('fuertewp_emails_fatal_error'); 589 $options['fuertewp_emails_automatic_updates'] = carbon_get_theme_option('fuertewp_emails_automatic_updates'); 590 $options['fuertewp_emails_comment_awaiting_moderation'] = carbon_get_theme_option('fuertewp_emails_comment_awaiting_moderation'); 591 $options['fuertewp_emails_comment_has_been_published'] = carbon_get_theme_option('fuertewp_emails_comment_has_been_published'); 592 $options['fuertewp_emails_user_reset_their_password'] = carbon_get_theme_option('fuertewp_emails_user_reset_their_password'); 593 $options['fuertewp_emails_user_confirm_personal_data_export_request'] = carbon_get_theme_option('fuertewp_emails_user_confirm_personal_data_export_request'); 594 $options['fuertewp_emails_new_user_created'] = carbon_get_theme_option('fuertewp_emails_new_user_created'); 595 $options['fuertewp_emails_network_new_site_created'] = carbon_get_theme_option('fuertewp_emails_network_new_site_created'); 596 $options['fuertewp_emails_network_new_user_site_registered'] = carbon_get_theme_option('fuertewp_emails_network_new_user_site_registered'); 597 $options['fuertewp_emails_network_new_site_activated'] = carbon_get_theme_option('fuertewp_emails_network_new_site_activated'); 598 $options['fuertewp_restrictions_restapi_loggedin_only'] = carbon_get_theme_option('fuertewp_restrictions_restapi_loggedin_only'); 599 $options['fuertewp_restrictions_restapi_disable_app_passwords'] = carbon_get_theme_option('fuertewp_restrictions_restapi_disable_app_passwords'); 600 $options['fuertewp_restrictions_disable_xmlrpc'] = carbon_get_theme_option('fuertewp_restrictions_disable_xmlrpc'); 601 $options['fuertewp_restrictions_htaccess_security_rules'] = carbon_get_theme_option('fuertewp_restrictions_htaccess_security_rules'); 602 $options['fuertewp_restrictions_disable_admin_create_edit'] = carbon_get_theme_option('fuertewp_restrictions_disable_admin_create_edit'); 603 $options['fuertewp_restrictions_disable_weak_passwords'] = carbon_get_theme_option('fuertewp_restrictions_disable_weak_passwords'); 604 $options['fuertewp_restrictions_force_strong_passwords'] = carbon_get_theme_option('fuertewp_restrictions_force_strong_passwords'); 605 $options['fuertewp_restrictions_disable_admin_bar_roles'] = carbon_get_theme_option('fuertewp_restrictions_disable_admin_bar_roles'); 606 $options['fuertewp_restrictions_restrict_permalinks'] = carbon_get_theme_option('fuertewp_restrictions_restrict_permalinks'); 607 $options['fuertewp_restrictions_restrict_acf'] = carbon_get_theme_option('fuertewp_restrictions_restrict_acf'); 608 $options['fuertewp_restrictions_disable_theme_editor'] = carbon_get_theme_option('fuertewp_restrictions_disable_theme_editor'); 609 $options['fuertewp_restrictions_disable_plugin_editor'] = carbon_get_theme_option('fuertewp_restrictions_disable_plugin_editor'); 610 $options['fuertewp_restrictions_disable_theme_install'] = carbon_get_theme_option('fuertewp_restrictions_disable_theme_install'); 611 $options['fuertewp_restrictions_disable_plugin_install'] = carbon_get_theme_option('fuertewp_restrictions_disable_plugin_install'); 612 $options['fuertewp_restrictions_disable_customizer_css'] = carbon_get_theme_option('fuertewp_restrictions_disable_customizer_css'); 613 $options['fuertewp_restricted_scripts'] = carbon_get_theme_option('fuertewp_restricted_scripts'); 614 $options['fuertewp_restricted_pages'] = carbon_get_theme_option('fuertewp_restricted_pages'); 615 $options['fuertewp_removed_menus'] = carbon_get_theme_option('fuertewp_removed_menus'); 616 $options['fuertewp_removed_submenus'] = carbon_get_theme_option('fuertewp_removed_submenus'); 617 $options['fuertewp_removed_adminbar_menus'] = carbon_get_theme_option('fuertewp_removed_adminbar_menus'); 618 } 619 620 return $options; 554 return Fuerte_Wp_Config::get_enforcer_config(); 621 555 } 622 556 -
fuerte-wp/trunk/includes/class-fuerte-wp-login-logger.php
r3395378 r3395451 548 548 public function get_remaining_attempts($ip, $username) 549 549 { 550 $max_attempts = (int) carbon_get_theme_option('fuertewp_login_max_attempts', 5);550 $max_attempts = (int)Fuerte_Wp_Config::get_field('login_max_attempts', 5); 551 551 552 552 $failed_count = $this->get_failed_attempts($ip, $username); -
fuerte-wp/trunk/includes/class-fuerte-wp-login-manager.php
r3395378 r3395451 392 392 // Show remaining attempts if any failed 393 393 $remaining = $this->logger->get_remaining_attempts($ip, $username); 394 $max_attempts = (int) carbon_get_theme_option('fuertewp_login_max_attempts', 5);394 $max_attempts = (int)Fuerte_Wp_Config::get_field('login_max_attempts', 5); 395 395 396 396 if ($remaining < $max_attempts) { … … 585 585 { 586 586 // Use the correct container ID ('Fuerte-WP') since we're using compact input 587 $enabled = carbon_get_theme_option('fuertewp_registration_enable', 'fuerte-wp');587 $enabled = Fuerte_Wp_Config::get_field('registration_enable', 'enabled'); 588 588 589 589 // Handle different Carbon Fields return formats
Note: See TracChangeset
for help on using the changeset viewer.