"""
Module de Contrôle d'Accès - Web Sentinel
=========================================

Détection des failles d'autorisation et de contrôle d'accès.
Couvre OWASP A01 - Broken Access Control.
"""

from typing import Iterable, List
import requests
from urllib.parse import urljoin, urlparse
import json

from web_sentinel.model import Finding, ScanRequest

def evaluate_access_control(request: ScanRequest) -> Iterable[Finding]:
    """
    Évalue les contrôles d'accès et détecte les failles d'autorisation.
    
    Tests effectués :
    - IDOR (Insecure Direct Object Reference)
    - Bypass d'autorisation vertical/horizontal
    - Accès aux endpoints admin sans authentification
    - Directory traversal
    """
    findings: List[Finding] = []
    
    if not request.allow_invasive:
        findings.append(Finding(
            check="access_control",
            title="Tests d'accès désactivés en mode passif",
            severity="info",
            description="Les tests de contrôle d'accès nécessitent le mode invasif.",
            remediation="Activer allow_invasive pour effectuer les tests d'autorisation.",
        ))
        return findings
    
    # Test des endpoints administratifs courants
    admin_endpoints = [
        "/admin", "/administrator", "/admin.php", "/admin/",
        "/wp-admin/", "/phpmyadmin/", "/adminer/",
        "/api/admin", "/api/v1/admin", "/management/",
        "/dashboard", "/control", "/panel"
    ]
    
    findings.extend(_test_admin_access(request, admin_endpoints))
    findings.extend(_test_idor_vulnerabilities(request))
    findings.extend(_test_directory_traversal(request))
    findings.extend(_test_privilege_escalation(request))
    
    return findings

def _test_admin_access(request: ScanRequest, endpoints: List[str]) -> List[Finding]:
    """Test l'accès aux endpoints administratifs sans authentification."""
    findings = []
    base_url = f"https://{request.domain}"
    
    for endpoint in endpoints:
        try:
            url = urljoin(base_url, endpoint)
            response = requests.get(url, timeout=request.timeout, allow_redirects=False)
            
            # Codes de réponse suspects
            if response.status_code in [200, 301, 302]:
                # Vérifier si c'est vraiment une interface admin
                admin_indicators = [
                    "admin", "dashboard", "login", "password", 
                    "user management", "settings", "configuration",
                    "phpmyadmin", "adminer"
                ]
                
                content_lower = response.text.lower()
                if any(indicator in content_lower for indicator in admin_indicators):
                    findings.append(Finding(
                        check="access_control",
                        title=f"Interface administrateur exposée : {endpoint}",
                        severity="high",
                        description=f"L'endpoint {url} semble être une interface administrateur accessible sans authentification.",
                        remediation="Protéger les interfaces administrateur par authentification forte et restriction d'accès par IP.",
                        evidence=f"HTTP {response.status_code} - Taille: {len(response.content)} bytes"
                    ))
                    
        except requests.exceptions.RequestException:
            continue
    
    return findings

def _test_idor_vulnerabilities(request: ScanRequest) -> List[Finding]:
    """Test les vulnérabilités IDOR sur les endpoints d'API."""
    findings = []
    base_url = f"https://{request.domain}"
    
    # Endpoints typiques vulnérables aux IDOR
    idor_patterns = [
        "/api/user/{id}",
        "/api/users/{id}",
        "/api/profile/{id}",
        "/api/account/{id}",
        "/api/order/{id}",
        "/api/document/{id}",
        "/user/{id}",
        "/profile/{id}",
        "/account/{id}"
    ]
    
    # Test avec différents IDs
    test_ids = ["1", "2", "admin", "0", "999999"]
    
    for pattern in idor_patterns:
        for test_id in test_ids:
            try:
                url = urljoin(base_url, pattern.format(id=test_id))
                response = requests.get(url, timeout=request.timeout)
                
                # Si l'endpoint retourne des données sans authentification
                if response.status_code == 200 and len(response.content) > 100:
                    # Vérifier si les données semblent sensibles
                    sensitive_keywords = [
                        "email", "phone", "address", "password", "token",
                        "credit", "ssn", "birth", "private"
                    ]
                    
                    content_lower = response.text.lower()
                    if any(keyword in content_lower for keyword in sensitive_keywords):
                        findings.append(Finding(
                            check="access_control",
                            title=f"Vulnérabilité IDOR détectée : {pattern}",
                            severity="critical",
                            description=f"L'endpoint {url} expose des données sensibles sans contrôle d'accès approprié.",
                            remediation="Implémenter des contrôles d'autorisation sur tous les endpoints d'accès aux données utilisateur.",
                            evidence=f"ID testé: {test_id} - Données sensibles exposées"
                        ))
                        
            except requests.exceptions.RequestException:
                continue
    
    return findings

def _test_directory_traversal(request: ScanRequest) -> List[Finding]:
    """Test les vulnérabilités de directory traversal."""
    findings = []
    base_url = f"https://{request.domain}"
    
    # Payloads de directory traversal
    traversal_payloads = [
        "../../../etc/passwd",
        "..\\..\\..\\windows\\system32\\drivers\\etc\\hosts",
        "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd",
        "....//....//....//etc/passwd",
        "..;/..;/..;/etc/passwd"
    ]
    
    # Paramètres susceptibles d'être vulnérables
    test_params = ["file", "path", "document", "page", "include", "dir"]
    
    for param in test_params:
        for payload in traversal_payloads:
            try:
                params = {param: payload}
                response = requests.get(base_url, params=params, timeout=request.timeout)
                
                # Signatures de fichiers système
                if any(signature in response.text for signature in [
                    "root:x:0:0", "/bin/bash", "daemon:x:", 
                    "# hosts file", "127.0.0.1", "localhost"
                ]):
                    findings.append(Finding(
                        check="access_control",
                        title=f"Directory Traversal détecté via {param}",
                        severity="critical",
                        description=f"Le paramètre '{param}' est vulnérable aux attaques de directory traversal.",
                        remediation="Valider et assainir tous les paramètres de fichier/chemin. Utiliser un whitelist des fichiers autorisés.",
                        evidence=f"Payload: {payload} - Fichier système exposé"
                    ))
                    break  # Un payload suffit pour ce paramètre
                    
            except requests.exceptions.RequestException:
                continue
    
    return findings

def _test_privilege_escalation(request: ScanRequest) -> List[Finding]:
    """Test l'escalade de privilèges via manipulation des paramètres."""
    findings = []
    base_url = f"https://{request.domain}"
    
    # Paramètres susceptibles de contrôler les privilèges
    privilege_params = [
        ("role", ["admin", "administrator", "root", "superuser"]),
        ("user_type", ["admin", "1", "administrator"]),
        ("permission", ["admin", "all", "*"]),
        ("access_level", ["admin", "9", "999"]),
        ("is_admin", ["true", "1", "yes"]),
        ("privilege", ["admin", "elevated", "root"])
    ]
    
    for param, values in privilege_params:
        for value in values:
            try:
                # Test en GET
                params = {param: value}
                response = requests.get(base_url, params=params, timeout=request.timeout)
                
                # Indicateurs d'élévation de privilèges réussie
                privilege_indicators = [
                    "admin panel", "administrative", "dashboard",
                    "user management", "delete user", "system settings"
                ]
                
                if any(indicator in response.text.lower() for indicator in privilege_indicators):
                    findings.append(Finding(
                        check="access_control",
                        title=f"Escalade de privilèges possible via {param}",
                        severity="critical",
                        description=f"Le paramètre '{param}={value}' semble permettre l'élévation de privilèges.",
                        remediation="Implémenter des contrôles d'autorisation côté serveur. Ne jamais se fier aux paramètres client.",
                        evidence=f"Paramètre: {param}={value}"
                    ))
                    
            except requests.exceptions.RequestException:
                continue
    
    return findings