o
    0i@                     @   s  d dl mZ d dlmZ d dlmZ d dlmZm	Z	 d dl
Z
d dlmZ d dlmZmZ d dlmZmZ d d	lmZ e
eZG d
d deZdedefddZdededefddZeh dZdefddZ dedB de!dedefddZ"dedefddZ#de$de!d ededB fd!d"Z%d efd#d$Z&	d5de$de!d ed%edB def
d&d'Z'd6d)ed*e$fd+d,Z(ed-geegd.d/ Z)ed0geegd1d2 Z*ed0geegd3d4 Z+dS )7    )get_user_model)check_password)settings)DatabaseErrorconnectionsN)Token)api_viewpermission_classes)AllowAnyIsAuthenticatedResponsec                   @   s   e Zd ZdS )LegacyDbUnavailableN)__name__
__module____qualname__ r   r   8/var/www/html/livekitdocker/backend/config/auth_views.pyr      s    r   hash_strreturnc                 C   s   |  drd| dd   S | S )Nz$2y$z$2b$   )
startswith)r   r   r   r   _normalize_bcrypt_hash   s   
r   plain_or_hashstored_hashc                 C   s   | |krdS | drDzdd l}W n ty    td Y dS w zt|}|| d|dW S  tyC   tjddd Y dS w t| |S )	NTz$2r   zauth.token bcrypt import failedFzutf-8zauth.token bcrypt check failedexc_info)	r   bcrypt	Exceptionloggerwarningr   checkpwencodedjango_check_password)r   r   r   
normalizedr   r   r   _check_bcrypt_password   s"   


r%   >   adminowner
superadminsuper_adminc                 C   s$   | d u rdS t |   ddS )N  _)strstriplowerreplace)role_valr   r   r   _normalize_role_string4   s   r2   bia_role_rawlegacyui_hintc                 C   sf   | durt |  dkrt| pdS |d}|dur)t | dkr)t|p(dS |r1t|p0dS dS )za
    Prefer cluster `business_id_agents.role`, then legacy `users.role`, then login UI hint.
    Nr*   agentrole)r-   r.   r2   get)r3   r4   r5   lrr   r   r   _coerce_effective_role:   s   
r:   	role_normc                 C   s   | t v S N)_ADMIN_ROLES)r;   r   r   r   _django_is_privilegedH   s   r>   business_id
identifierc              
   C   s  t |dpd  }t |pd  }|d}|d}g }|dur8t | dkr8|d| |gf |durMt | dkrM|d| |gf |sQ|rm|d| |pX||p[|gf |d	| |pf||pi|gf z\td
  K}|D ]>\}	}
z1||	|
 | }|r|d durt |d  dkrt |d  W   W  d   W S W qw t	y   Y qww W d   W dS 1 sw   Y  W dS  t	y   Y dS w )z
    Read dashboard role from cluster `business_id_agents.role` when that column (and
    identifying columns) exist. Schema may vary; unsupported columns are skipped.
    emailr*   agent_iduser_idNzTSELECT role FROM business_id_agents WHERE business_id = %s AND agent_id = %s LIMIT 1zSSELECT role FROM business_id_agents WHERE business_id = %s AND user_id = %s LIMIT 1z
                SELECT role FROM business_id_agents
                WHERE business_id = %s
                  AND LOWER(TRIM(COALESCE(email, ''))) IN (%s, %s)
                LIMIT 1
                z
                SELECT role FROM business_id_agents
                WHERE business_id = %s
                  AND LOWER(TRIM(COALESCE(username, ''))) IN (%s, %s)
                LIMIT 1
                clusterr   )
r-   r8   r.   r/   appendr   cursorexecutefetchoner   )r?   r4   r@   
email_norm
ident_normrB   rC   attemptscursqlparamsrowr   r   r   #_fetch_role_from_business_id_agentsL   sb   

$rP   c              
   C   s   d}z?t d  /}||| | g | }|s"	 W d   W dS dd |jD }tt||}W d   n1 s;w   Y  W n ty[ } ztj	ddd t
t||d}~ww |d	pbd
 }|rm|dvrmdS |S )z
    Query the legacy master DB `users` table.
    Expected columns (common Laravel-style): user_id, username, email, password, role, business_id, agent_id, status.
    z
        SELECT user_id, username, email, password, role, business_id, agent_id, status, name
        FROM users
        WHERE (email = %s OR username = %s)
        LIMIT 1
    defaultNc                 S   s   g | ]}|d  qS )r   r   ).0cr   r   r   
<listcomp>   s    z&_fetch_legacy_user.<locals>.<listcomp>z0legacy master DB unavailable while fetching userTr   statusr*   )active1enabled)r   rF   rG   rH   descriptiondictzipr   r   errorr   r-   r8   r/   )r@   rM   rL   rO   colnamesdatae
status_valr   r   r   _fetch_legacy_user   s*   ra   effective_rolec                 C   s  ddl m}m} t|p|dpd}t|dpd  }t|p$d  }z-td 	 }	|	
d| g |	 rG	 W d   W d	S W d   n1 sQw   Y  W n	 ty`   Y nw || \}
}}}zXtd 	 G}	|	
d
||
 d| g |	 s	 W d   W dS |tv r	 W d   W d	S |	
d||
 d| ||g |	 duW  d   W S 1 sw   Y  W dS  ty   Y dS w )a0  
    Cluster may store agents either in Django's mapping table `business_id_agents`
    or in per-business tables `{business_id}_agents` (legacy / Laravel-style).

    Agents logging in via the dashboard should appear in the per-business agents
    table with the same email when that table is used.
    r   )_table_names_q_identr7   r*   rA   rD   z?SELECT 1 FROM business_id_agents WHERE business_id = %s LIMIT 1NTzSELECT 1 FROM z WHERE business_id = %s LIMIT 1Fz
                SELECT 1 FROM z
                WHERE business_id = %s
                  AND (
                    LOWER(email) = %s
                    OR LOWER(email) = %s
                  )
                LIMIT 1
                )apps.cluster.dynamic_tablesrc   rd   r2   r8   r-   r.   r/   r   rF   rG   rH   r   r=   )r?   r4   r@   rb   rc   rd   
role_lowerrI   rJ   rL   agents_tabler,   r   r   r   _cluster_acknowledges_business   sV   
		
(rh     messagerU   c                 C   s   t | g| d|dS )N)non_field_errorsrj   rU   r   )rj   rU   r   r   r   _auth_error   s   
rm   POSTc                 C   s~  | j dp| j dpd }| j dpd}| j dpd }td||t| |r0|s4tdS zt|}W no ty } zcz<t	j
dpJi  }t	j
d	pTi  }|d
 d|d d|d }|d
 d|d d|d }	W n ty   d}d}	Y nw td| d|	 dt|j dddW  Y d}~S d}~ww |std| tdS |dpd }
td|t|pd|
dd  |
rt||
std||d|d tdS |d}|du rtd||d tdS zt|}W n ty   td  Y S w t|||}t|||}t||||d!s8td"||d| td#S t }t|dpI|dpI|dd$ }|jj|d%\}}|r_|  t|dpgd|_t|}||_||_|jg d&|s~g ndg d' tjj|d(\}}t |j!t|dp|j"t|d)p|dpdt|dpd||d*|g d+d,S )-z
    Custom token endpoint compatible with your React UI + legacy MySQL tables.
    Body: { "username": "...", "password": "...", "role": "admin|agent" }
    Returns: { "token": "...", "user": {...} }
    usernamerA   r*   passwordr7   z8auth.token attempt identifier=%s role=%s has_password=%szMissing username or password.rQ   rD   HOST:PORTz / NAMEz	<unknown>zLegacy database is not reachable from this backend runtime. Check network/firewall and ensure the running process loaded the correct DB URLs. (default=z
; cluster=z	) (error=)i  rl   Nz:auth.token legacy user not found/blocked for identifier=%sz+Unable to log in with provided credentials.zHauth.token password_check identifier=%s password_len=%s stored_prefix=%s   z=auth.token password mismatch identifier=%s user_id=%s role=%srC   r?   z7auth.token missing business_id identifier=%s user_id=%szUser is missing business_id.zInvalid business_id.)rb   zGauth.token cluster check failed identifier=%s user_id=%s business_id=%szRNo matching agent configuration in the cluster database for this user or business.   )ro   )rA   is_staffis_superuser)update_fields)usernamerB   )idr|   rA   r7   agentIdr?   permissions)tokenr{   )#r^   r8   r.   r   r    boolrm   ra   r   r   	DATABASEScopyr   typer   lenr%   intrP   r:   rh   r   r-   objectsget_or_createset_unusable_passwordrA   r>   rx   ry   saver   r   keyr}   )requestr@   rp   r7   r4   r_   dfltclus	dflt_hint	clus_hintr   r?   business_id_intr3   rf   
DjangoUserdjango_usernamedjango_usercreated
privilegedr   r,   r   r   r   legacy_token   s    &*


( r   GETc           
   
   C   s,  | j }t|ddst|ddrdnd}t|dd p t|dd p d }|r)t|nd }d }d }d }|}|rl|d	}|d
}|d}|d urZzt|}W n tyY   d }Y nw |d urdt|||nd }	t|	|d}t	t
|put|ddt|ddt|ddt|ddd  pt|dd|||g dS )Nrx   Fry   r&   r6   rA   ro   r*   r?   rB   rC   r}   get_full_namec                   S   s   dS )Nr*   r   r   r   r   r   <lambda>  s    zme.<locals>.<lambda>)r}   ro   rA   r|   r7   r~   r?   r   )r{   getattrr.   ra   r8   r   r   rP   r:   r   r-   )
r   udj_roleidentr4   r?   rB   legacy_user_idrole_outbiar   r   r   mef  sD     




r   c              
   C   s  | j }t|ddpt|ddpd }|rt|nd}|r"|dnd}z|dur-t|nd}W n ty;   d}Y nw |sFtddidd	S td
 	 u}z6|
d|g | }|sktddidd	W W  d   S t|d |d |d |d dW W  d   S  ty   |
d|g | }|stddidd	 Y W  d   S t|d |d |d dd Y W  d   S w 1 sw   Y  dS )z
    Return current user's business record fields needed by the dashboard.
    Source of truth: legacy master DB table `businesses`, column `plans`.
    rA   Nro   r*   r?   rj   z$Business not found for current user.i  rl   rQ   zdSELECT business_id, business_name, plans, ch_language FROM businesses WHERE business_id = %s LIMIT 1zBusiness not found.r            )r?   business_nameplansch_languagezWSELECT business_id, business_name, plans FROM businesses WHERE business_id = %s LIMIT 1)r{   r   r.   ra   r8   r   r   r   r   rF   rG   rH   r   )r   r   r   r4   r?   r   rL   rO   r   r   r   business_plan  s`    
r   r<   )ri   ),django.contrib.authr   django.contrib.auth.hashersr   r#   django.confr   	django.dbr   r   loggingrest_framework.authtoken.modelsr   rest_framework.decoratorsr   r	   rest_framework.permissionsr
   r   rest_framework.responser   	getLoggerr   r   RuntimeErrorr   r-   r   r   r%   	frozensetr=   r2   rZ   r:   r>   r   rP   ra   rh   rm   r   r   r   r   r   r   r   <module>   sR    
@#
7x&