#!/usr/bin/env python3
"""
ivanti_auth.py — Inicia sesión en Ivanti Pulse Secure y guarda la cookie DSID.

Uso:
  python3 ivanti_auth.py               → Pide user/pass/totp, guarda cookie
  python3 ivanti_auth.py --status      → Muestra si hay sesión activa
  python3 ivanti_auth.py --export      → Imprime export para usar con eval
  eval $(python3 ivanti_auth.py --export)

La cookie se guarda en ~/.ivanti_dsid
Cualquier script puede leerla: dsid = open("~/.ivanti_dsid").read().strip()
Cuando caduque (recibirás 401/403), simplemente vuelve a ejecutar este script.

Requisitos:
  - sudo apt install openconnect
  - pip install pyotp --break-system-packages  (solo si usas --auto-totp)
"""

import subprocess
import re
import os
import sys
import argparse
import getpass
from pathlib import Path
from datetime import datetime

# ---------------------------------------------------------------------------
# Configuración
# ---------------------------------------------------------------------------
IVANTI_HOST = os.environ.get("IVANTI_HOST", "https://extranet.scayle.es")
COOKIE_FILE = Path(os.environ.get("IVANTI_COOKIE_FILE", "~/.ivanti_dsid")).expanduser()
TOTP_SECRET = os.environ.get("IVANTI_TOTP_SECRET", "")
# Pin del certificado del servidor — evita que openconnect pregunte si confiar
SERVERCERT = os.environ.get("IVANTI_SERVERCERT", "pin-sha256:9A/fcDHvLl6SQYXDM/nVcr7ynMsrj0jLLF+M4C/3YUw=")


# ---------------------------------------------------------------------------
# Autenticación
# ---------------------------------------------------------------------------

def authenticate(auto_totp: bool = False) -> str:
    """Autentica en Ivanti. Pide user/pass/totp por terminal. Devuelve DSID."""
    print(f"\n{'=' * 50}")
    print(f"  Autenticación Ivanti — {IVANTI_HOST}")
    print(f"{'=' * 50}")
    user = input("  Usuario: ").strip()
    password = getpass.getpass("  Contraseña: ")

    if auto_totp and TOTP_SECRET:
        try:
            import pyotp
            totp_code = pyotp.TOTP(TOTP_SECRET).now()
            print(f"  TOTP (auto): {totp_code}")
        except ImportError:
            sys.exit("Error: pip install pyotp --break-system-packages")
    else:
        totp_code = input("  Código TOTP: ").strip()

    # Ivanti espera contraseña+TOTP concatenados
    combined = f"{password}{totp_code}"

    cmd = [
        "sudo", "openconnect",
        "--authenticate",
        "--protocol=pulse",
        f"--user={user}",
        f"--servercert={SERVERCERT}",
        IVANTI_HOST,
    ]

    print(f"\n[*] Autenticando...")

    proc = subprocess.Popen(
        cmd,
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
    )

    stdout, stderr = proc.communicate(input=f"{combined}\n", timeout=60)

    # Buscar DSID en la salida
    dsid = None
    for line in stdout.splitlines():
        line = line.strip()
        if line.startswith("COOKIE="):
            cookie_val = line.split("=", 1)[1].strip("'\"")
            match = re.search(r"DSID=([^;]+)", cookie_val)
            dsid = match.group(1) if match else cookie_val
            break

    if not dsid:
        print(f"\n[ERROR] No se pudo extraer DSID.", file=sys.stderr)
        print(f"  stdout: {stdout}", file=sys.stderr)
        print(f"  stderr: {stderr}", file=sys.stderr)
        sys.exit(1)

    # Guardar en fichero (solo el valor, nada más)
    COOKIE_FILE.write_text(dsid)
    COOKIE_FILE.chmod(0o600)

    print(f"\n[+] ✅ Sesión iniciada")
    print(f"[+] Cookie guardada en {COOKIE_FILE}")
    print(f"[+] Cuando caduque, vuelve a ejecutar este script")
    return dsid


# ---------------------------------------------------------------------------
# Utilidades
# ---------------------------------------------------------------------------

def cmd_status():
    """Muestra si hay cookie guardada."""
    if not COOKIE_FILE.exists():
        print("❌ No hay sesión. Ejecuta:  python3 ivanti_auth.py")
        return

    dsid = COOKIE_FILE.read_text().strip()
    if not dsid:
        print("❌ Fichero de cookie vacío. Ejecuta:  python3 ivanti_auth.py")
        return

    mtime = datetime.fromtimestamp(COOKIE_FILE.stat().st_mtime)
    age_min = (datetime.now() - mtime).seconds // 60

    print(f"\n  Estado de sesión Ivanti")
    print(f"  {'─' * 40}")
    print(f"  DSID:     {dsid[:16]}...{dsid[-8:]}")
    print(f"  Creada:   {mtime.strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"  Hace:     {age_min} min")
    print(f"  Fichero:  {COOKIE_FILE}")
    print(f"\n  ⚠ No sabemos si sigue válida hasta que la uses.")
    print(f"  Si recibes 401/403, vuelve a ejecutar:  python3 ivanti_auth.py")


def cmd_export():
    """Imprime exports para eval."""
    if not COOKIE_FILE.exists():
        print("# No hay sesión. Ejecuta:  python3 ivanti_auth.py", file=sys.stderr)
        sys.exit(1)

    dsid = COOKIE_FILE.read_text().strip()
    print(f'export IVANTI_DSID="{dsid}"')
    print(f'export IVANTI_HOST="{IVANTI_HOST}"')


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

def main():
    parser = argparse.ArgumentParser(
        description="Inicia sesión en Ivanti y guarda la cookie DSID",
        epilog="Cuando la cookie caduque (401/403), ejecuta de nuevo este script.",
    )
    parser.add_argument("--status", "-s", action="store_true",
                        help="Mostrar estado de la sesión")
    parser.add_argument("--export", "-e", action="store_true",
                        help="Imprimir exports (uso: eval $(python3 ivanti_auth.py --export))")
    parser.add_argument("--auto-totp", action="store_true",
                        help="Generar TOTP automáticamente (requiere IVANTI_TOTP_SECRET)")

    args = parser.parse_args()

    if args.status:
        cmd_status()
    elif args.export:
        cmd_export()
    else:
        authenticate(auto_totp=args.auto_totp)


if __name__ == "__main__":
    main()
