<?php
/**
 * Name: Password Protection
 * Description: Password-protect entire site from public view with a single shared password that all visitors must enter before accessing any content or pages
 * Category: Security
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "Development Site Protection",
 *     "description": "Password-protect staging site from public view while allowing developers and clients to access with shared password."
 *   },
 *   {
 *     "title": "Pre-Launch Privacy",
 *     "description": "Hide work-in-progress site from search engines and public while building, giving only select people the access password."
 *   },
 *   {
 *     "title": "Temporary Maintenance",
 *     "description": "Restrict entire site to password holders during major updates or content changes without affecting logged-in admins."
 *   }
 * ]
 * Added: 2025-08-12
 * Tags: password, protection, security, privacy, access-control
 * WhereToFind: {
 *   "location": "Configure in module settings",
 *   "url": "",
 *   "instructions": "Set password - entire site protected for non-logged-in users",
 *   "newTab": false
 * }
 */

// Prevent direct access
if (!defined('ABSPATH')) exit;

class SwitchboardPasswordProtection {

    public function __construct() {
        add_action('template_redirect', [$this, 'check_password']);
        add_action('admin_bar_menu', [$this, 'add_admin_bar_notice'], 100);
        add_action('admin_head', [$this, 'add_admin_bar_styles']);
        add_action('wp_head', [$this, 'add_admin_bar_styles']);
    }

    public function check_password() {
        // Skip for logged-in users
        if (is_user_logged_in()) {
            return;
        }

        // Skip for login page
        if (isset($GLOBALS['pagenow']) && $GLOBALS['pagenow'] === 'wp-login.php') {
            return;
        }

        $settings = get_option('switchboard_password-protection_settings', []);
        $password = isset($settings['password']) ? $settings['password'] : '';

        if (empty($password)) {
            return;
        }

        // Use transients instead of sessions - with proper sanitization
        $remote_addr = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
        $session_key = 'switchboard_password_' . md5($remote_addr . $user_agent);

        // Rate limiting: Check failed attempts
        $rate_limit_key = 'switchboard_pw_attempts_' . md5($remote_addr);
        $failed_attempts = get_transient($rate_limit_key);

        if ($failed_attempts && $failed_attempts >= 5) {
            // Locked out for 15 minutes after 5 failed attempts
            $this->show_password_form(true);
            exit;
        }

        // Check if password submitted
        if (isset($_POST['site_password'], $_POST['_wpnonce'])) {
            if (wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), 'switchboard_password')) {
                $submitted_password = sanitize_text_field(wp_unslash($_POST['site_password']));

                // Validate password is not empty
                if (empty($submitted_password)) {
                    $this->increment_failed_attempts($rate_limit_key);
                    $this->show_password_form(false);
                    exit;
                }

                // Use timing-safe comparison to prevent timing attacks
                if (hash_equals($password, $submitted_password)) {
                    // Clear failed attempts on success
                    delete_transient($rate_limit_key);
                    set_transient($session_key, true, DAY_IN_SECONDS);

                    $redirect_to = isset($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : home_url();
                    wp_safe_redirect($redirect_to);
                    exit;
                } else {
                    // Increment failed attempts
                    $this->increment_failed_attempts($rate_limit_key);
                    $this->show_password_form(false);
                    exit;
                }
            }
        }

        // Check if already verified
        if (get_transient($session_key)) {
            return;
        }

        // Show password form
        $this->show_password_form(false);
        exit;
    }

    /**
     * Increment failed login attempts for rate limiting
     */
    private function increment_failed_attempts($rate_limit_key) {
        $failed_attempts = get_transient($rate_limit_key);
        $failed_attempts = $failed_attempts ? $failed_attempts + 1 : 1;
        set_transient($rate_limit_key, $failed_attempts, 15 * MINUTE_IN_SECONDS);
    }

    /**
     * Add admin bar notice when password protection is active
     */
    public function add_admin_bar_notice($wp_admin_bar) {
        // Only show to users who can manage options
        if (!current_user_can('manage_options')) {
            return;
        }

        $settings = get_option('switchboard_password-protection_settings', []);
        $password = isset($settings['password']) ? $settings['password'] : '';

        if (empty($password)) {
            return;
        }

        $wp_admin_bar->add_node([
            'id' => 'password-protection-status',
            'title' => esc_html__('Password Protected', 'wpswitchboard'),
            'href' => admin_url('admin.php?page=switchboard'),
            'meta' => [
                'class' => 'password-protection-warning',
                'title' => esc_attr__('Site is password protected - Click to manage settings', 'wpswitchboard')
            ]
        ]);
    }

    /**
     * Add custom CSS styling for the admin bar notice (red color)
     */
    public function add_admin_bar_styles() {
        // Only add styles for users who can see the notice
        if (!current_user_can('manage_options')) {
            return;
        }

        $settings = get_option('switchboard_password-protection_settings', []);
        $password = isset($settings['password']) ? $settings['password'] : '';

        if (empty($password)) {
            return;
        }

        ?>
        <style type="text/css">
            #wpadminbar #wp-admin-bar-password-protection-status > .ab-item {
                background-color: #dc2626 !important;
                color: #ffffff !important;
            }
            #wpadminbar #wp-admin-bar-password-protection-status:hover > .ab-item {
                background-color: #b91c1c !important;
                color: #ffffff !important;
            }
        </style>
        <?php
    }

    private function show_password_form($locked_out = false) {
        // Add security headers
        header('X-Frame-Options: DENY');
        header('X-Content-Type-Options: nosniff');
        header('X-XSS-Protection: 1; mode=block');
        header('Referrer-Policy: no-referrer');

        ?>
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title><?php echo esc_html__('Password Protected', 'wpswitchboard'); ?></title>
            <style>
                body {
                    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
                    background: #2563eb;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    min-height: 100vh;
                    margin: 0;
                }
                .password-form {
                    background: white;
                    padding: 40px;
                    border-radius: 10px;
                    box-shadow: 0 10px 40px rgba(0,0,0,0.2);
                    max-width: 400px;
                    width: 90%;
                }
                h1 {
                    margin: 0 0 10px;
                    font-size: 24px;
                    color: #333;
                }
                p {
                    color: #666;
                    margin: 0 0 20px;
                }
                input[type="password"] {
                    width: 100%;
                    padding: 12px;
                    border: 2px solid #ddd;
                    border-radius: 6px;
                    font-size: 16px;
                    box-sizing: border-box;
                }
                input[type="password"]:focus {
                    outline: none;
                    border-color: #2563eb;
                }
                button {
                    width: 100%;
                    padding: 12px;
                    background: #2563eb;
                    color: white;
                    border: none;
                    border-radius: 6px;
                    font-size: 16px;
                    font-weight: 600;
                    cursor: pointer;
                    margin-top: 15px;
                }
                button:hover {
                    background: #1d4ed8;
                }
                .error-message {
                    background: #fee2e2;
                    color: #991b1b;
                    padding: 12px;
                    border-radius: 6px;
                    margin-bottom: 20px;
                    font-size: 14px;
                    border-left: 4px solid #dc2626;
                }
            </style>
        </head>
        <body>
            <div class="password-form">
                <h1><?php echo esc_html__('Password Protected', 'wpswitchboard'); ?></h1>
                <?php if ($locked_out): ?>
                    <div class="error-message">
                        <strong><?php echo esc_html__('Too Many Failed Attempts', 'wpswitchboard'); ?></strong><br>
                        <?php echo esc_html__('Your access has been temporarily blocked due to multiple failed login attempts. Please try again in 15 minutes.', 'wpswitchboard'); ?>
                    </div>
                <?php else: ?>
                    <p><?php echo esc_html__('This site is password protected. Please enter the password to continue.', 'wpswitchboard'); ?></p>
                    <form method="post" autocomplete="off">
                        <?php wp_nonce_field('switchboard_password'); ?>
                        <input type="password" name="site_password" placeholder="<?php echo esc_attr__('Enter password', 'wpswitchboard'); ?>" required autofocus autocomplete="off">
                        <button type="submit"><?php echo esc_html__('Access Site', 'wpswitchboard'); ?></button>
                    </form>
                <?php endif; ?>
            </div>
        </body>
        </html>
        <?php
    }

    /**
     * Render settings panel for Password Protection
     */
    public function render_settings($settings = []) {
        $password = isset($settings['password']) ? $settings['password'] : '';
        ?>
        <input type="hidden" name="module" value="password-protection">

        <div class="sb-sp-info-box sb-sp-info-box-info" style="margin-bottom: 24px;">
            <h4 class="sb-sp-heading-sm">How It Works</h4>
            <p class="sb-sp-description" style="margin: 0;">Set a password to protect your entire site from public access. All visitors (except logged-in users) will be required to enter the password before accessing any content.</p>
        </div>

        <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 24px;">
            <div class="sb-sp-field-horizontal-left">
                <label for="setting_password" class="sb-sp-label">
                    <?php echo esc_html__('Site Password', 'wpswitchboard'); ?>
                </label>
                <p class="sb-sp-description">
                    <?php echo esc_html__('Set a password to protect your entire site. Leave empty to disable protection. Logged-in users bypass this protection automatically.', 'wpswitchboard'); ?>
                </p>
            </div>
            <input type="password" name="setting_password" id="setting_password" value="<?php echo esc_attr($password); ?>" class="sb-sp-input" placeholder="Enter password to enable protection">
        </div>

        <div class="sb-sp-info-box sb-sp-info-box-tip">
            <h4 class="sb-sp-heading-sm">Important Notes</h4>
            <ul class="sb-sp-list">
                <li class="sb-sp-list-item"><strong>Logged-in users:</strong> Administrators and logged-in users can always access the site without entering the password.</li>
                <li class="sb-sp-list-item"><strong>Per-page protection:</strong> WordPress has built-in per-page password protection. Edit any page/post and look for the "Password" field in Quick Edit, Classic Editor, or Gutenberg sidebar.</li>
                <li class="sb-sp-list-item"><strong>Rate limiting:</strong> After 5 failed attempts, access is temporarily blocked for 15 minutes to prevent brute force attacks.</li>
            </ul>
        </div>
        <?php
    }

    /**
     * Register data locations for cleanup system
     */
    public function register_data_locations() {
        return [
            'options' => [
                'switchboard_password-protection_settings',
            ],
            'option_patterns' => [
                '_transient_switchboard_password_%',
                '_transient_timeout_switchboard_password_%',
                '_transient_switchboard_pw_attempts_%',
                '_transient_timeout_switchboard_pw_attempts_%',
            ],
            'tables' => [],
            'post_meta' => [],
            'user_meta' => [],
        ];
    }
}

$switchboard_instance = new SwitchboardPasswordProtection();
Switchboard_Data_Registry::get_instance()->auto_register($switchboard_instance, __FILE__);
