<?php
/**
 * Name: Obfuscate Author Slugs
 * Description: Hide author usernames in URLs and replace with numeric IDs to prevent username enumeration attacks and protect against brute force login attempts targeting usernames
 * Category: Security
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "Prevent Username Enumeration",
 *     "description": "Replace author usernames in URLs with random hashes (e.g., /author/john becomes /author/a4f7d2e1b9) to block brute force attacks."
 *   },
 *   {
 *     "title": "Security Through Obscurity",
 *     "description": "Hide admin/editor usernames from public author archives to make it harder for attackers to target specific accounts."
 *   },
 *   {
 *     "title": "Multi-Author Blog Protection",
 *     "description": "Secure contributor and author usernames on blogs with 10+ writers from being discovered via /?author=1 enumeration."
 *   }
 * ]
 * WhereToFind: {
 *   "location": "Works automatically",
 *   "url": "",
 *   "instructions": "Author URLs use random hashes instead of usernames",
 *   "newTab": false
 * }
 * Added: 2025-07-28
 * Tags: security, author, username, obfuscation, enumeration
 */

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

class SwitchboardObfuscateAuthorSlugs {

    public function __construct() {
        add_filter('author_link', [$this, 'obfuscate_author_link'], 10, 3);
        add_filter('request', [$this, 'handle_obfuscated_request']);
    }

    /**
     * Register data locations for cleanup system
     *
     * @return array Data storage locations
     */
    public function register_data_locations() {
        return [
            'options' => [],
            'option_patterns' => [],
            'tables' => [],
            'post_meta' => [],
            'user_meta' => [],
        ];
    }
    
    private function get_obfuscated_slug($author_id) {
        $cache_key = 'switchboard_author_slug_' . $author_id;
        $slug = wp_cache_get($cache_key);

        if ($slug === false) {
            $salt = defined('AUTH_KEY') && AUTH_KEY ? AUTH_KEY : 'switchboard_fallback_salt';
            $hash = md5($author_id . $salt);
            $slug = substr($hash, 0, 10);
            wp_cache_set($cache_key, $slug, '', HOUR_IN_SECONDS);
        }

        return $slug;
    }
    
    public function obfuscate_author_link($link, $author_id, $author_nicename) {
        $obfuscated_slug = $this->get_obfuscated_slug($author_id);
        return str_replace($author_nicename, $obfuscated_slug, $link);
    }
    
    public function handle_obfuscated_request($query_vars) {
        if (isset($query_vars['author_name'])) {
            $slug = $query_vars['author_name'];
            
            // Check cache first
            $cache_key = 'switchboard_author_reverse_' . $slug;
            $author_id = wp_cache_get($cache_key);
            
            if ($author_id === false) {
                // Try to find user by obfuscated slug
                // Limit to 1000 users to prevent performance issues
                $users = get_users(['fields' => 'ID', 'number' => 1000]);
                foreach ($users as $user_id) {
                    if ($slug === $this->get_obfuscated_slug($user_id)) {
                        $author_id = $user_id;
                        wp_cache_set($cache_key, $author_id, '', HOUR_IN_SECONDS);
                        break;
                    }
                }
            }
            
            if ($author_id) {
                $query_vars['author'] = $author_id;
                unset($query_vars['author_name']);
            }
        }
        
        return $query_vars;
    }
}

// Initialize and register with data system
$switchboard_instance = new SwitchboardObfuscateAuthorSlugs();
Switchboard_Data_Registry::get_instance()->auto_register($switchboard_instance, __FILE__);
