o
    $ahtG                     @   s  d Z ddlZddlZddlZddlZddlmZ ddlmZ ddl	m
Z
 ddlmZ ddlmZ ddlmZ eeZd	d
 Zdd Zdd Zdd Zdd Zd.ddZd/ddZd.ddZdd Zdd Zdd Zd0d!d"Z d1d$d%Z!d0d&d'Z"d(d) Z#d2d*d+Z$d.d,d-Z%dS )3z
Authentication Utilities Module

This module contains utility functions for authentication and authorization
including IP detection, user agent parsing, location detection, and security helpers.

Author: Senior Django Developer
Date: 2024
    N)urlparse)settings)cache)timezone)	timedelta)parsec                 C   sZ   g d}|D ]}| j |}|r%d|v r|dd  }t|r%|  S q| j ddS )a&  
    Get the real IP address of the client from the request.
    
    This function handles various proxy configurations and headers
    to extract the actual client IP address.
    
    Args:
        request: Django HTTP request object
        
    Returns:
        str: Client IP address
    )ZHTTP_CF_CONNECTING_IPZHTTP_X_FORWARDED_FORZHTTP_X_REAL_IPZHTTP_X_FORWARDEDZHTTP_X_CLUSTER_CLIENT_IPZHTTP_FORWARDED_FORZHTTP_FORWARDEDREMOTE_ADDR,r   r   z	127.0.0.1N)METAgetsplitstripis_valid_ip)requestZ
ip_headersheaderip r   OC:\Users\vibe-look\OneDrive\Desktop\Adtlas_V\src\apps\authentification\utils.pyget_client_ip   s   r   c                 C   sf   d}d}g d}| r|   dv rdS t|| st|| r1tjs/|D ]}t|| r. dS q#dS dS )z
    Validate if a string is a valid IP address (IPv4 or IPv6).
    
    Args:
        ip (str): IP address string to validate
        
    Returns:
        bool: True if valid IP, False otherwise
    z[^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$z5^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::1$|^::$)^127\.^10\.^172\.(1[6-9]|2[0-9]|3[0-1])\.^192\.168\.^169\.254\.^::1$^fe80:)unknownnone FTN)lowerrematchr   DEBUG)r   Zipv4_patternZipv6_patternprivate_patternspatternr   r   r   r   D   s   
r   c                 C   s   | sdddddddddd	S z>t | }d}|jrd}n|jr!d}n|jr'd}n|jr,d}||jjp2d|jjp7d|jjp<d|jjpAd|j|j|j|jd	W S  t	yu } zt
d|  d	|  dddddddddd	W  Y d
}~S d
}~ww )z
    Parse user agent string to extract device, browser, and OS information.
    
    Args:
        user_agent_string (str): Raw user agent string
        
    Returns:
        dict: Parsed user agent information
    UnknownF)	device_typebrowserZbrowser_versionosZ
os_version	is_mobile	is_tabletis_pcis_botZMobileZTabletDesktopZBotzError parsing user agent "z": N)parse_user_agentr)   r*   r+   r,   r'   familyversion_stringr(   	Exceptionloggerwarning)Zuser_agent_string
user_agentr&   er   r   r   get_user_agent_infoo   s\   




r6   c                 C   s8  ddddddddd}t | rt| r|S d|  }t|}|r#|S zRtjd|  dd	d
id}|jdkrt| }|ddkrt|dd|dd|dd|dd|d|d|dd|ddd}t||d |W S W n ty } zt	
d|  d|  W Y d}~nd}~ww t||d |S )aR  
    Get geographical location information from IP address.
    
    This function uses a free IP geolocation service to get location data.
    Results are cached to avoid excessive API calls.
    
    Args:
        ip_address (str): IP address to lookup
        
    Returns:
        dict: Location information (country, city, etc.)
    r%   ZXXNUTC)countryZcountry_codecityregionZlatitudeZ	longituder   ispZip_location_zhttp://ip-api.com/json/   fieldsz;status,country,countryCode,region,city,lat,lon,timezone,isp)timeoutparams   statussuccessr8   ZcountryCoder9   r:   ZlatZlonr   r;   iQ zError getting location for IP z: i  )r   is_private_ipr   r   requestsstatus_codejsonsetr1   r2   r3   )
ip_addressZdefault_location	cache_keyZcached_locationresponsedataZlocation_infor5   r   r   r   get_location_from_ip   sV   








"rL   c                 C   s,   g d}|D ]}t || t jr dS qdS )z
    Check if an IP address is private/local.
    
    Args:
        ip_address (str): IP address to check
        
    Returns:
        bool: True if private IP, False otherwise
    )r   r   r   r   r   r   r   z^localhost$TFN)r    r!   
IGNORECASE)rH   r#   r$   r   r   r   rC      s   
rC   c                 C   s   | sdS |   } | drdS | drdS z t| }|jr&|jdvr&W dS |js,W dS |r4|j|v W S W dS  ty@   Y dS w )a`  
    Check if a URL is safe for redirection.
    
    This function prevents open redirect vulnerabilities by validating
    that the URL is safe to redirect to.
    
    Args:
        url (str): URL to validate
        allowed_hosts (list): List of allowed hosts (optional)
        
    Returns:
        bool: True if URL is safe, False otherwise
    Fz///T)httphttpsN)r   
startswithr   schemenetlocr1   )urlallowed_hostsparsedr   r   r   is_safe_url  s&   

rW       c                    s>   ddl ddl}|j|j d  d fddt| D S )z
    Generate a cryptographically secure random token.
    
    Args:
        length (int): Length of the token to generate
        
    Returns:
        str: Secure random token
    r   Nz-_r   c                 3   s    | ]}  V  qd S N)choice).0_alphabetsecretsr   r   	<genexpr>W  s    z(generate_secure_token.<locals>.<genexpr>)r_   stringascii_lettersdigitsjoinrange)lengthra   r   r]   r   generate_secure_tokenH  s   
rg   c                 C      ddl m} || |S )z
    Hash a password with salt using Django's password hashing.
    
    Args:
        password (str): Password to hash
        salt (str): Optional salt (if None, Django will generate one)
        
    Returns:
        str: Hashed password
    r   )make_passwordN)django.contrib.auth.hashersri   )passwordsaltri   r   r   r   hash_password_with_saltZ     
rm   c                 C   rh   )z
    Verify a password against its hash.
    
    Args:
        password (str): Plain text password
        hashed_password (str): Hashed password to verify against
        
    Returns:
        bool: True if password matches, False otherwise
    r   )check_passwordN)rj   ro   )rk   Zhashed_passwordro   r   r   r   verify_passwordi  rn   rp   c              	   C   sx   t | dkttd| ttd| ttd| ttd| t|  d}t| |d< t| d |d	< |S )z
    Check if a password meets strength requirements.
    
    Args:
        password (str): Password to check
        
    Returns:
        dict: Dictionary with strength check results
       z[A-Z]z[a-z]z\dz[!@#$%^&*(),.?":{}|<>])rf   Z	uppercaseZ	lowercasedigitspecialZ	no_commonZ	is_strong   scoreN)lenboolr    searchis_common_passwordallvaluessum)rk   checksr   r   r   is_password_strongx  s   
	r~   c                 C   s   h d}|   |v S )z
    Check if a password is in the list of common passwords.
    
    Args:
        password (str): Password to check
        
    Returns:
        bool: True if password is common, False otherwise
    >   ZdragonZ123456ZsupermanZshadowadminrk   ZbaseballZ	123456789ZjordanZmasterZmonkeyZpassword123ZmichaelZfootballZprincessZ	liverpoolZabc123ZwelcomeZqwertyZletmeinNr   )rk   Zcommon_passwordsr   r   r   ry     s   ry   <   c                 C   s<   t  }|j|j| | ddd}d| d|  d|  S )a$  
    Generate a cache key for rate limiting.
    
    Args:
        identifier (str): Unique identifier (IP, user ID, etc.)
        action (str): Action being rate limited
        window_minutes (int): Time window in minutes
        
    Returns:
        str: Cache key for rate limiting
    r   )minutesecondmicrosecondZrate_limit_r\   N)r   nowreplacer   	timestamp)
identifieractionwindow_minutesr   Zwindow_startr   r   r   rate_limit_key  s   r   r<   c                 C   s    t | ||}t|d}||kS )ar  
    Check if an action is rate limited for a given identifier.
    
    Args:
        identifier (str): Unique identifier (IP, user ID, etc.)
        action (str): Action being checked
        max_attempts (int): Maximum attempts allowed
        window_minutes (int): Time window in minutes
        
    Returns:
        bool: True if rate limited, False otherwise
    r   N)r   r   r   )r   r   max_attemptsr   rI   Zcurrent_attemptsr   r   r   is_rate_limited  s   r   c                 C   sJ   t | ||}zt|dd }t|||d  |W S  ty$   Y dS w )a5  
    Increment the rate limit counter for a given identifier and action.
    
    Args:
        identifier (str): Unique identifier (IP, user ID, etc.)
        action (str): Action being incremented
        window_minutes (int): Time window in minutes
        
    Returns:
        int: New attempt count
    r   rt   r   N)r   r   r   rG   r1   )r   r   r   rI   Z	new_countr   r   r   increment_rate_limit  s   r   c                  C   sv   ddl m} m} | jjt d}| }|  |jjt d}| }|  t	
d| d| d ||dS )	z
    Clean up expired tokens from the database.
    
    This function should be called periodically (e.g., via a cron job)
    to remove expired tokens and keep the database clean.
    rt   )PasswordResetTokenEmailVerificationToken)expires_at__ltzCleaned up z# expired password reset tokens and z" expired email verification tokens)Zpassword_reset_tokensZemail_verification_tokensN)modelsr   r   objectsfilterr   r   countdeleter2   info)r   r   Zexpired_reset_tokensZreset_countZexpired_verification_tokensZverification_countr   r   r   clean_expired_tokens  s&   r   c                 C   sL   | t   |r|jnd|r|jnd||pi d}tdt|  dS )a  
    Log security-related events for monitoring and auditing.
    
    Args:
        event_type (str): Type of security event
        user: User instance (optional)
        ip_address (str): IP address (optional)
        details (dict): Additional event details (optional)
    N)
event_typer   user_id
user_emailrH   detailszSECURITY_EVENT: )	r   r   	isoformatidemailr2   r3   rF   dumps)r   userrH   r   Zlog_datar   r   r   log_security_event
  s   

r   c                    s   |du rg d}t | ts| S |  }| D ]D\ }t fdd|D rOt |trJt|dkrJ|dd  dt|d   |dd  | < qd	| < qt |tr[t||| < q|S )
z
    Mask sensitive data in dictionaries for logging.
    
    Args:
        data (dict): Data to mask
        fields_to_mask (list): List of field names to mask
        
    Returns:
        dict: Data with sensitive fields masked
    N)rk   tokensecretkeyauthZ
credentialsessioncsrfc                 3   s    | ]	}|   v V  qd S rY   r   )r[   Z	sensitiver   r   r   r`   9  s    z&mask_sensitive_data.<locals>.<genexpr>      *z***)
isinstancedictcopyitemsanystrrv   mask_sensitive_data)rK   Zfields_to_maskZmasked_datavaluer   r   r   r   !  s   
2

r   rY   )rX   )r   )r<   r   )NNN)&__doc__r    rF   loggingrD   urllib.parser   django.confr   django.core.cacher   django.utilsr   datetimer   Zuser_agentsr   r.   	getLogger__name__r2   r   r   r6   rL   rC   rW   rg   rm   rp   r~   ry   r   r   r   r   r   r   r   r   r   r   <module>   s:   

)+AI

3




"