'; printf( /* translators: %s: WordPress version number */ esc_html__('Absoluto Access Gate requires WordPress 5.0 or higher. You are running WordPress %s. Please upgrade WordPress to activate this plugin.', 'absoluto-access-gate'), esc_html(get_bloginfo('version')) ); echo '

'; }); return; } // Check PHP version if (version_compare(PHP_VERSION, '7.0', '<')) { add_action('admin_notices', function() { echo '

'; printf( /* translators: %s: PHP version number */ esc_html__('Absoluto Access Gate requires PHP 7.0 or higher. You are running PHP %s. Please upgrade PHP to activate this plugin.', 'absoluto-access-gate'), esc_html(PHP_VERSION) ); echo '

'; }); return; } // Define plugin constants define('ABSACGA_VERSION', '1.0.0'); define('ABSACGA_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('ABSACGA_PLUGIN_URL', plugin_dir_url(__FILE__)); define('ABSACGA_MIN_WP_VERSION', '5.0'); define('ABSACGA_MIN_PHP_VERSION', '7.0'); class Absoluto_Access_Gate { /** * Instance of this class */ private static $instance = null; /** * Get instance of this class */ public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ private function __construct() { // Hook into WordPress add_action('template_redirect', array($this, 'force_login_redirect')); add_action('admin_menu', array($this, 'add_admin_menu')); add_action('admin_init', array($this, 'register_settings')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); add_action('login_message', array($this, 'display_login_message')); add_action('admin_bar_menu', array($this, 'add_admin_bar_notice'), 999); // Add settings link to plugin actions $plugin_file = plugin_basename(__FILE__); add_filter("plugin_action_links_{$plugin_file}", array($this, 'add_plugin_action_links')); } /** * Force login redirect */ public function force_login_redirect() { // Check if plugin is enabled $plugin_enabled = get_option('absacga_enabled', '1'); if ($plugin_enabled !== '1') { return; } // Don't redirect if user is logged in and has bypass permission if (is_user_logged_in() && $this->can_bypass_login()) { return; } // Don't redirect if current page is excluded if ($this->is_page_excluded()) { return; } // Don't redirect if post type is excluded if ($this->is_post_type_excluded()) { return; } // Don't redirect if category/tag is excluded if ($this->is_taxonomy_excluded()) { return; } // Don't redirect RSS feeds if excluded if ($this->is_rss_excluded() && (is_feed() || is_comment_feed())) { return; } // Don't redirect REST API if excluded if ($this->is_rest_api_excluded() && $this->is_rest_api_request()) { return; } // Check bypass key/token if ($this->has_bypass_key()) { return; } // Check IP whitelist if ($this->is_ip_whitelisted()) { return; } // Check if maintenance mode is enabled if ($this->is_maintenance_mode()) { $this->show_maintenance_page(); return; } // Don't redirect AJAX requests if option is enabled if ($this->exclude_ajax() && wp_doing_ajax()) { return; } // Don't redirect archive/search pages if excluded if ($this->is_archive_excluded() && (is_archive() || is_search() || is_404())) { return; } // Don't redirect if already on login page if (is_admin() || $this->is_login_page()) { return; } // Redirect to login page if (!is_user_logged_in()) { // Sanitize current URL $protocol = is_ssl() ? 'https://' : 'http://'; $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : ''; $request_uri = isset($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : ''; $current_url = $protocol . $host . $request_uri; $redirect_back = get_option('absacga_redirect_back', '1'); $custom_redirect = get_option('absacga_custom_redirect_url', ''); // Get custom login page URL $custom_login_url = get_option('absacga_custom_login_url', ''); if (!empty($custom_login_url)) { // Validate custom login URL $custom_login_url = esc_url_raw($custom_login_url); // Use custom login URL if ($redirect_back === '1') { $redirect_url = add_query_arg('redirect_to', rawurlencode($current_url), $custom_login_url); } elseif (!empty($custom_redirect)) { $redirect_url = add_query_arg('redirect_to', rawurlencode(esc_url_raw($custom_redirect)), $custom_login_url); } else { $redirect_url = add_query_arg('redirect_to', rawurlencode(home_url()), $custom_login_url); } } else { // Use default WordPress login if ($redirect_back === '1') { $redirect_url = wp_login_url($current_url); } elseif (!empty($custom_redirect)) { $redirect_url = wp_login_url(esc_url_raw($custom_redirect)); } else { $redirect_url = wp_login_url(home_url()); } } // Allow filtering of redirect URL $redirect_url = apply_filters('absacga_login_redirect_url', $redirect_url, $current_url); // Sanitize final redirect URL $redirect_url = esc_url_raw($redirect_url); wp_safe_redirect($redirect_url); exit; } } /** * Check if user can bypass login requirement */ private function can_bypass_login() { $bypass_roles = get_option('absacga_bypass_roles', array()); $bypass_users = get_option('absacga_bypass_users', array()); // Check user roles if (!empty($bypass_roles)) { $user = wp_get_current_user(); if (!empty($user->roles)) { foreach ($user->roles as $role) { if (in_array($role, $bypass_roles)) { return true; } } } } // Check specific users if (!empty($bypass_users)) { $current_user_id = get_current_user_id(); if (in_array($current_user_id, array_map('intval', $bypass_users))) { return true; } } return false; } /** * Check if current page is excluded */ private function is_page_excluded() { $excluded_pages = get_option('absacga_excluded_pages', array()); if (empty($excluded_pages)) { return false; } // Check if current page/post ID is excluded if (is_singular()) { $current_id = get_queried_object_id(); if (in_array($current_id, array_map('intval', $excluded_pages))) { return true; } } // Check if current page template is excluded $excluded_templates = get_option('absacga_excluded_templates', array()); if (!empty($excluded_templates) && is_page()) { $template = get_page_template_slug(); if (!empty($template) && in_array($template, $excluded_templates)) { return true; } } return false; } /** * Check if current page is login page */ private function is_login_page() { return in_array($GLOBALS['pagenow'], array('wp-login.php', 'wp-register.php')); } /** * Check if current post type is excluded */ private function is_post_type_excluded() { $excluded_post_types = get_option('absacga_excluded_post_types', array()); if (empty($excluded_post_types)) { return false; } if (is_singular()) { $post_type = get_post_type(); if (in_array($post_type, $excluded_post_types)) { return true; } } if (is_post_type_archive()) { $post_type = get_post_type(); if (in_array($post_type, $excluded_post_types)) { return true; } } return false; } /** * Check if current taxonomy term is excluded */ private function is_taxonomy_excluded() { $excluded_categories = get_option('absacga_excluded_categories', array()); $excluded_tags = get_option('absacga_excluded_tags', array()); if (empty($excluded_categories) && empty($excluded_tags)) { return false; } if (is_category()) { $category_id = get_queried_object_id(); if (in_array($category_id, array_map('intval', $excluded_categories))) { return true; } } if (is_tag()) { $tag_id = get_queried_object_id(); if (in_array($tag_id, array_map('intval', $excluded_tags))) { return true; } } if (is_singular()) { // Check if post belongs to excluded category $categories = wp_get_post_categories(get_the_ID()); if (!empty($categories) && !empty($excluded_categories)) { foreach ($categories as $cat_id) { if (in_array($cat_id, array_map('intval', $excluded_categories))) { return true; } } } // Check if post has excluded tag $tags = wp_get_post_tags(get_the_ID(), array('fields' => 'ids')); if (!empty($tags) && !empty($excluded_tags)) { foreach ($tags as $tag_id) { if (in_array($tag_id, array_map('intval', $excluded_tags))) { return true; } } } } return false; } /** * Check if RSS feeds are excluded */ private function is_rss_excluded() { return get_option('absacga_exclude_rss', '0') === '1'; } /** * Check if REST API is excluded */ private function is_rest_api_excluded() { return get_option('absacga_exclude_rest_api', '0') === '1'; } /** * Check if current request is REST API */ private function is_rest_api_request() { if (defined('REST_REQUEST') && REST_REQUEST) { return true; } if (!isset($_SERVER['REQUEST_URI'])) { return false; } $rest_prefix = trailingslashit(rest_get_url_prefix()); $request_uri = sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])); return strpos($request_uri, $rest_prefix) !== false; } /** * Check if bypass key is present in URL */ private function has_bypass_key() { $bypass_key = get_option('absacga_bypass_key', ''); if (empty($bypass_key)) { return false; } // Validate bypass key from URL parameter (public-facing, no nonce needed) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public URL parameter, not form data if (!isset($_GET['absacga_bypass'])) { return false; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public URL parameter, not form data $key_param = sanitize_text_field(wp_unslash($_GET['absacga_bypass'])); // Use hash_equals for timing-safe comparison return hash_equals($bypass_key, $key_param); } /** * Check if current IP is whitelisted */ private function is_ip_whitelisted() { $whitelisted_ips = get_option('absacga_whitelist_ips', ''); if (empty($whitelisted_ips)) { return false; } $current_ip = $this->get_client_ip(); $ips = array_map('trim', explode("\n", $whitelisted_ips)); foreach ($ips as $ip) { if (empty($ip)) { continue; } // Support CIDR notation if (strpos($ip, '/') !== false) { if ($this->ip_in_range($current_ip, $ip)) { return true; } } else { if ($current_ip === $ip) { return true; } } } return false; } /** * Get client IP address */ private function get_client_ip() { $ip_keys = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR'); foreach ($ip_keys as $key) { if (isset($_SERVER[$key]) && !empty($_SERVER[$key])) { $ip_value = sanitize_text_field(wp_unslash($_SERVER[$key])); foreach (explode(',', $ip_value) as $ip) { $ip = trim($ip); // Validate IP address if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) { return $ip; } } } } // Fallback to REMOTE_ADDR if (isset($_SERVER['REMOTE_ADDR'])) { $remote_addr = sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])); if (filter_var($remote_addr, FILTER_VALIDATE_IP) !== false) { return $remote_addr; } } return '0.0.0.0'; } /** * Check if IP is in CIDR range */ private function ip_in_range($ip, $range) { if (strpos($range, '/') === false) { return $ip === $range; } list($subnet, $mask) = explode('/', $range); $ip_long = ip2long($ip); $subnet_long = ip2long($subnet); $mask_long = -1 << (32 - (int)$mask); return ($ip_long & $mask_long) === ($subnet_long & $mask_long); } /** * Check if maintenance mode is enabled */ private function is_maintenance_mode() { return get_option('absacga_maintenance_mode', '0') === '1'; } /** * Show maintenance page */ private function show_maintenance_page() { $maintenance_message = get_option('absacga_maintenance_message', __('We are currently performing scheduled maintenance. Please check back soon.', 'absoluto-access-gate')); $maintenance_title = get_option('absacga_maintenance_title', __('Site Under Maintenance', 'absoluto-access-gate')); status_header(503); nocache_headers(); ?> > <?php echo esc_html($maintenance_title); ?>

' . esc_html__('Settings', 'absoluto-access-gate') . ''; array_unshift($links, $settings_link); return $links; } /** * Display custom login message */ public function display_login_message($message) { $login_message = get_option('absacga_login_message', ''); if (!empty($login_message)) { $message .= '

' . esc_html($login_message) . '

'; } return $message; } /** * Add admin bar notice */ public function add_admin_bar_notice($wp_admin_bar) { if (!current_user_can('manage_options')) { return; } $enabled = get_option('absacga_enabled', '1'); if ($enabled === '1') { $wp_admin_bar->add_node(array( 'id' => 'absacga-active', 'title' => ' ' . esc_html__('Access Gate Active', 'absoluto-access-gate'), 'href' => esc_url(admin_url('options-general.php?page=absoluto-access-gate')), 'meta' => array( 'title' => esc_html__('Absoluto Access Gate is active', 'absoluto-access-gate') ) )); } } /** * Register settings */ public function register_settings() { // Register settings with sanitization callbacks for defense in depth register_setting('absacga_settings', 'absacga_enabled', array('sanitize_callback' => array($this, 'sanitize_checkbox'))); register_setting('absacga_settings', 'absacga_excluded_pages', array('sanitize_callback' => array($this, 'sanitize_int_array'))); register_setting('absacga_settings', 'absacga_excluded_templates', array('sanitize_callback' => array($this, 'sanitize_text_array'))); register_setting('absacga_settings', 'absacga_excluded_post_types', array('sanitize_callback' => array($this, 'sanitize_key_array'))); register_setting('absacga_settings', 'absacga_excluded_categories', array('sanitize_callback' => array($this, 'sanitize_int_array'))); register_setting('absacga_settings', 'absacga_excluded_tags', array('sanitize_callback' => array($this, 'sanitize_int_array'))); register_setting('absacga_settings', 'absacga_exclude_rss', array('sanitize_callback' => array($this, 'sanitize_checkbox'))); register_setting('absacga_settings', 'absacga_exclude_rest_api', array('sanitize_callback' => array($this, 'sanitize_checkbox'))); register_setting('absacga_settings', 'absacga_bypass_roles', array('sanitize_callback' => array($this, 'sanitize_key_array'))); register_setting('absacga_settings', 'absacga_bypass_users', array('sanitize_callback' => array($this, 'sanitize_int_array'))); register_setting('absacga_settings', 'absacga_redirect_back', array('sanitize_callback' => array($this, 'sanitize_checkbox'))); register_setting('absacga_settings', 'absacga_custom_redirect_url', array('sanitize_callback' => 'esc_url_raw')); register_setting('absacga_settings', 'absacga_custom_login_url', array('sanitize_callback' => 'esc_url_raw')); register_setting('absacga_settings', 'absacga_login_message', array('sanitize_callback' => 'sanitize_textarea_field')); register_setting('absacga_settings', 'absacga_bypass_key', array('sanitize_callback' => 'sanitize_text_field')); register_setting('absacga_settings', 'absacga_whitelist_ips', array('sanitize_callback' => 'sanitize_textarea_field')); register_setting('absacga_settings', 'absacga_maintenance_mode', array('sanitize_callback' => array($this, 'sanitize_checkbox'))); register_setting('absacga_settings', 'absacga_maintenance_title', array('sanitize_callback' => 'sanitize_text_field')); register_setting('absacga_settings', 'absacga_maintenance_message', array('sanitize_callback' => 'sanitize_textarea_field')); register_setting('absacga_settings', 'absacga_exclude_ajax', array('sanitize_callback' => array($this, 'sanitize_checkbox'))); register_setting('absacga_settings', 'absacga_exclude_archives', array('sanitize_callback' => array($this, 'sanitize_checkbox'))); } /** * Sanitize checkbox value */ public function sanitize_checkbox($value) { return ($value === '1' || $value === 1 || $value === true) ? '1' : '0'; } /** * Sanitize integer array */ public function sanitize_int_array($value) { if (!is_array($value)) { return array(); } return array_map('absint', $value); } /** * Sanitize text array */ public function sanitize_text_array($value) { if (!is_array($value)) { return array(); } return array_map('sanitize_text_field', $value); } /** * Sanitize key array */ public function sanitize_key_array($value) { if (!is_array($value)) { return array(); } return array_map('sanitize_key', $value); } /** * Enqueue admin scripts */ public function enqueue_admin_scripts($hook) { if ($hook !== 'settings_page_absoluto-access-gate') { return; } // Enqueue WordPress dashicons wp_enqueue_style('dashicons'); // Enqueue WordPress jQuery (default library per Guideline 13) wp_enqueue_script('jquery'); // Enqueue custom admin styles wp_add_inline_style('dashicons', $this->get_admin_styles()); // Convert multi-select dropdowns to user-friendly checkbox lists wp_add_inline_script('jquery', $this->get_checkbox_selector_script()); // IP whitelist functionality (works with or without Select2) wp_add_inline_script('jquery', ' jQuery(document).ready(function($) { // Add current IP to whitelist $("#absacga-add-current-ip").on("click", function(e) { e.preventDefault(); var currentIP = $("#absacga-current-ip").data("ip"); var textarea = $("#absacga_whitelist_ips"); var currentValue = textarea.val().trim(); var ipLines = currentValue ? currentValue.split("\\n") : []; // Check if IP already exists var ipExists = false; for (var i = 0; i < ipLines.length; i++) { if (ipLines[i].trim() === currentIP) { ipExists = true; break; } } if (!ipExists && currentIP && currentIP !== "0.0.0.0") { // Add IP to textarea if (currentValue) { textarea.val(currentValue + "\\n" + currentIP); } else { textarea.val(currentIP); } // Show success feedback var button = $(this); var originalText = button.text(); button.text("' . esc_js(__('Added!', 'absoluto-access-gate')) . '").css("background-color", "#34a853").css("color", "#fff"); setTimeout(function() { button.text(originalText).css("background-color", "").css("color", ""); }, 2000); } else if (ipExists) { // Show already exists feedback var button = $(this); var originalText = button.text(); button.text("' . esc_js(__('Already added', 'absoluto-access-gate')) . '").css("background-color", "#ff9800").css("color", "#fff"); setTimeout(function() { button.text(originalText).css("background-color", "").css("color", ""); }, 2000); } }); }); '); } /** * Get checkbox selector JavaScript */ private function get_checkbox_selector_script() { return ' jQuery(document).ready(function($) { // Convert all multi-select dropdowns to checkbox lists $("select[multiple]").each(function() { var $select = $(this); var selectId = $select.attr("id"); var selectName = $select.attr("name"); // Create checkbox container var $container = $("
").addClass("absacga-checkbox-selector").attr("data-select-id", selectId); // Create search box var $searchBox = $("") .attr("type", "text") .addClass("absacga-checkbox-search") .attr("placeholder", "' . esc_js(__('Search...', 'absoluto-access-gate')) . '"); // Create select all/none buttons var $buttonContainer = $("
").addClass("absacga-checkbox-buttons"); var $selectAll = $("


Absoluto Designs