o
    i`$                     @   s   U d Z ddlZddlZddlZddlmZ ddlmZmZm	Z	 ddl
mZ ddlmZ eeZdedefd	d
Zdeeef de	e fddZeG dd dZG dd dZG dd dZdae	e ed< defddZdddZdS )a  
API key authentication and caching for Veena3 TTS.

Features:
- In-memory API key cache with TTL
- Fast validation (< 5ms target)
- Graceful bypass mode for development
- Header extraction (Bearer token or X-API-Key)

Usage:
    from veena3modal.api.auth import get_api_validator, extract_api_key
    
    validator = get_api_validator()
    api_key = extract_api_key(request.headers)
    result = validator.validate(hash_api_key(api_key))
    
    if not result.is_valid:
        return 401/403 error with result.error_code
    N)	dataclass)DictAnyOptional)Lock)
get_loggerapi_keyreturnc                 C   s   t | d S )z
    Hash an API key for storage/lookup.
    
    Uses SHA-256 for consistent, secure hashing.
    
    Args:
        api_key: The raw API key
    
    Returns:
        Hex-encoded hash of the key
    zutf-8)hashlibsha256encode	hexdigest)r    r   //home/ubuntu/veenaModal/veena3modal/api/auth.pyhash_api_key!   s   r   headersc                 C   sV   dd |   D }|dd}| dr|dd  S |d}|r)| S dS )	a  
    Extract API key from request headers.
    
    Checks (in order of precedence):
    1. Authorization: Bearer <key>
    2. X-API-Key: <key>
    
    Args:
        headers: Request headers (case-insensitive keys)
    
    Returns:
        API key string or None if not found
    c                 S   s   i | ]	\}}|  |qS r   )lower).0kvr   r   r   
<dictcomp>?   s    z#extract_api_key.<locals>.<dictcomp>authorization zbearer    Nz	x-api-key)itemsgetr   
startswithstrip)r   
normalizedauth_headerr   r   r   r   extract_api_key0   s   
r    c                   @   sb   e Zd ZU dZeed< dZee ed< dZ	e
ed< dZeed< dZee ed	< dZee ed
< dS )ValidationResultzResult of API key validation.is_validNuser_id<   
rate_limit        credits
error_codeerror_message)__name__
__module____qualname____doc__bool__annotations__r#   r   strr%   intr'   floatr(   r)   r   r   r   r   r!   N   s   
 r!   c                   @   sx   e Zd ZdZddefddZdedeeee	f  fdd	Z
ded
eee	f ddfddZdeddfddZdddZdS )ApiKeyCachez
    In-memory cache for API key data.
    
    Thread-safe with configurable TTL.
    
    Attributes:
        ttl_seconds: Time-to-live for cache entries (default: 30s)
       ttl_secondsc                 C   s   || _ i | _i | _t | _dS )zc
        Initialize cache.
        
        Args:
            ttl_seconds: Cache entry TTL
        N)r5   _cache_timestampsr   _lock)selfr5   r   r   r   __init__d   s   zApiKeyCache.__init__key_hashr	   c                 C   s   | j < || jvr	 W d   dS | j|d}t | | jkr3| j|= | j|= 	 W d   dS | j| W  d   S 1 sBw   Y  dS )z
        Get cached key data.
        
        Args:
            key_hash: Hashed API key
        
        Returns:
            Key data dict or None if not found/expired
        Nr   )r8   r6   r7   r   timer5   )r9   r;   	cached_atr   r   r   r   p   s   

$zApiKeyCache.getdataNc                 C   sD   | j  || j|< t | j|< W d   dS 1 sw   Y  dS )z
        Cache key data.
        
        Args:
            key_hash: Hashed API key
            data: Key data to cache
        N)r8   r6   r<   r7   )r9   r;   r>   r   r   r   set   s   
"zApiKeyCache.setc                 C   sH   | j  | j|d | j|d W d   dS 1 sw   Y  dS )zRemove a key from cache.N)r8   r6   popr7   )r9   r;   r   r   r   
invalidate   s   "zApiKeyCache.invalidatec                 C   s@   | j  | j  | j  W d   dS 1 sw   Y  dS )zClear all cached entries.N)r8   r6   clearr7   )r9   r   r   r   rB      s   
"zApiKeyCache.clear)r4   r	   N)r*   r+   r,   r-   r1   r:   r0   r   r   r   r   r?   rA   rB   r   r   r   r   r3   Z   s    	r3   c                   @   s>   e Zd ZdZ		ddee defddZded	e	fd
dZ
dS )ApiKeyValidatora  
    API key validator with caching.
    
    Validates keys against cached data, checking:
    - Key exists and is active
    - Sufficient credits available
    
    Attributes:
        cache: ApiKeyCache instance
        bypass_mode: If True, all keys are considered valid
    NFcachebypass_modec                 C   s   |pt  | _|| _dS )z
        Initialize validator.
        
        Args:
            cache: Cache instance (creates new if None)
            bypass_mode: Skip validation (for development)
        N)r3   rE   rF   )r9   rE   rF   r   r   r   r:      s   
zApiKeyValidator.__init__r;   r	   c              	   C   sv  t   }| jrtdddddS | j|}|du r6t   | d }tjdd	d
t|ddd td	dddS |dd	sat   | d }tjdd	d|dt|ddd td	|ddddS |dd}|dkrt   | d }tjdd	d|d|t|ddd td	|d|dddS t   | d }tjdd|d|t|ddd td|d|d d!|dS )"z
        Validate an API key.
        
        Args:
            key_hash: Hashed API key
        
        Returns:
            ValidationResult with is_valid flag and details
        Tbypass_useri'  g    ~.A)r"   r#   r%   r'   Ni  api_key_validationF	not_found   )validreasonduration_msextraINVALID_API_KEYzAPI key not found or invalid)r"   r(   r)   	is_activeinactiver#   )rK   rL   r#   rM   zAPI key is inactive)r"   r#   r(   r)   r'   r&   r   
no_credits)rK   rL   r#   r'   rM   INSUFFICIENT_CREDITSzInsufficient credits)r"   r#   r'   r(   r)   )rK   r#   r'   rM   r%   r$   )r<   rF   r!   rE   r   loggerinforound)r9   r;   startkey_datarM   r'   r   r   r   validate   s   
	
	

zApiKeyValidator.validate)NF)r*   r+   r,   r-   r   r3   r.   r:   r0   r!   rZ   r   r   r   r   rD      s    
rD   _api_validatorc                  C   s^   t du r-tjdd dk} ttjdd}t|d}t|| da tj	d	| |d
d t S )a  
    Get the singleton API key validator.
    
    Configuration via environment variables:
    - AUTH_BYPASS_MODE: "true" to skip validation (dev only)
    - AUTH_CACHE_TTL: Cache TTL in seconds (default: 30)
    
    Returns:
        Configured ApiKeyValidator instance
    NAUTH_BYPASS_MODEfalsetrueAUTH_CACHE_TTL30)r5   )rE   rF   api_validator_initialized)rF   	cache_ttlrN   )
r[   osenvironr   r   r1   r3   rD   rU   rV   )rF   rb   rE   r   r   r   get_api_validator)  s   
re   c                   C   s   da dS )z"Reset the singleton (for testing).N)r[   r   r   r   r   reset_api_validatorH  s   rf   rC   )r-   rc   r<   r
   dataclassesr   typingr   r   r   	threadingr   veena3modal.shared.loggingr   r*   rU   r0   r   r    r!   r3   rD   r[   r/   re   rf   r   r   r   r   <module>   s&    G 