+
    in=                        ^ RI Ht ^ RIHt ^ RIHtHt ^ RIt^ RI	H
t
 ^ RIHtHt ^ RIHtHt ^ RIHt ]P&                  ! ]4      t ! R	 R
]4      tR R ltR R lt]! 0 R"m4      tR R ltR R ltR R ltR R ltR R lt R#R R llt!R$R R llt"]! R.4      ]! ].4      R 4       4       t#]! R.4      ]! ].4      R  4       4       t$]! R.4      ]! ].4      R! 4       4       t%R# )%    )get_user_model)check_password)DatabaseErrorconnectionsN)Token)api_viewpermission_classes)AllowAnyIsAuthenticatedResponsec                       ] tR t^tRtR# )LegacyDbUnavailable N)__name__
__module____qualname____firstlineno____static_attributes__r       ?E:\live-kit-agent\livekit_voicebot\backend\config\auth_views.pyr   r      s    r   r   c                0    V ^8  d   QhR\         R\         /# )   hash_strreturnstr)formats   "r   __annotate__r      s      S S r   c                 T    V P                  R 4      '       d   RV R,          ,           # V # )z$2y$z$2b$:   NN)
startswith)r   s   &r   _normalize_bcrypt_hashr#      s)     6""$$Or   c                <    V ^8  d   QhR\         R\         R\        /# )r   plain_or_hashstored_hashr   r   bool)r   s   "r   r   r      s!     = =# =C =D =r   c                 x   W8X  d   R # VP                  R4      '       dB    ^ RIp \        T4      pTP                  T P                  R4      TP                  R4      4      # \        W4      #   \         d    \        P	                  R4        R# i ; i  \         d    \        P	                  RR R7        R# i ; i)Tz$2Nz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,   
normalizeds   &&  r   _check_bcrypt_passwordr4      s    #d##	
	/<J>>-"6"6w"?ARARSZA[\\
 !<<  	NN<=	  	NN;dNK	s"   A, :B , BB"B98B9c                $    V ^8  d   QhR\         /# )r   r   r   )r   s   "r   r   r   3   s     ; ; ;r   c                 |    V f   R# \        V 4      P                  4       P                  4       P                  RR4      # )N  _)r   striplowerreplace)role_vals   &r   _normalize_role_stringr>   3   s4    x= &&(00c::r   c                V    V ^8  d   QhR\         R,          R\        R\         R\         /# )r   bia_role_rawNlegacyui_hintr   )r   dict)r   s   "r   r   r   9   s-      t T C TW r   c                >   V e4   \        V 4      P                  4       R8w  d   \        V 4      ;'       g    R# VP                  R4      pVe4   \        V4      P                  4       R8w  d   \        V4      ;'       g    R# V'       d   \        V4      ;'       g    R# R# )zY
Prefer cluster `business_id_agents.role`, then legacy `users.role`, then login UI hint.
r7   agentrole)r   r:   r>   get)r@   rA   rB   lrs   &&& r   _coerce_effective_rolerI   9   s     C$5$;$;$=$C%l3>>w>	F	B	~#b'--/R/%b)44W4%g.99'9r   c                0    V ^8  d   QhR\         R\        /# )r   	role_normr   r'   )r   s   "r   r   r   G   s     % %S %T %r   c                     V \         9   # N)_ADMIN_ROLES)rK   s   &r   _django_is_privilegedrO   G   s    $$r   c          	      V    V ^8  d   QhR\         R\        R\        R\        R,          /# )r   business_idrA   
identifierr   N)intrC   r   )r   s   "r   r   r   K   s0     = =S =$ =TW =\_bf\f =r   c                   \        VP                  R4      ;'       g    R4      P                  4       P                  4       p\        T;'       g    R4      P                  4       P                  4       pVP                  R4      pVP                  R4      p. pVe3   \        V4      P                  4       R8w  d   VP	                  RW.34       Ve3   \        V4      P                  4       R8w  d   VP	                  RW.34       V'       g	   V'       dS   TP	                  RY;'       g    TT;'       g    T.34       TP	                  R	Y;'       g    TT;'       g    T.34        \
        R
,          P                  4       ;_uu_ 4       pV F  w  r VP                  W4       VP                  4       pV'       da   V^ ,          eT   \        V^ ,          4      P                  4       R8w  d-   \        V^ ,          4      P                  4       u uuRRR4       # K  K  K  	  RRR4       R#   \         d     K  i ; i  + '       g   i     R# ; i  \         d     R# i ; i)z
Read dashboard role from cluster `business_id_agents.role` when that column (and
identifying columns) exist. Schema may vary; unsupported columns are skipped.
emailr7   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
                cluster)
r   rG   r:   r;   appendr   cursorexecutefetchoner   )rQ   rA   rR   
email_norm
ident_normrV   rW   attemptscursqlparamsrows   &&&         r   #_fetch_role_from_business_id_agentsrd   K   s   
 VZZ(..B/557==?JZ%%2&,,.446Jzz*%Hjj#G')HH 3 3 5 ;f'	
 s7|113r9e&	
 Z 66J
8P8PjQ
	
 	 66J
8P8PjQ
	
#**,,'KK,,,.Cs1v1c#a&k6G6G6IR6O"3q6{0022 -,
 7P1s	  ( - 	 %  -,   sa   %#I H:A8H(	H:
I H:I (H7	3H:6H7	7H::I	I I IIc                $    V ^8  d   QhR\         /# )r   rR   r   )r   s   "r   r   r      s      3 r   c                \   Rp \         R,          P                  4       ;_uu_ 4       pVP                  WV .4       VP                  4       pV'       g    RRR4       R# VP                   Uu. uF  qD^ ,          NK  	  pp\        \        WS4      4      pRRR4       XP                  R4      ;'       g    RP                  4       pT'       d
   TR	9  d   R# T# u upi   + '       g   i     LQ; i  \         d2   p\        P                  RRR7       \        \        T4      4      ThRp?ii ; i)
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
    defaultNz0legacy master DB unavailable while fetching userTr*   statusr7   )active1enabled)r   rZ   r[   r\   descriptionrC   zipr   r.   errorr   r   rG   r;   )	rR   ra   r`   rc   ccolnamesdatae
status_vals	   &        r   _fetch_legacy_userrt      s    
C1#**,,KK*56,,.C	 -, '*oo6o!oH6H*+D - ((8$**113Jj(BBK 7 -,  1GRVW!#a&)q01sR   #C/ -CC/ C,C=CC/ CC,	'C/ ,C/ /D+:,D&&D+c          
      b    V ^8  d   QhR\         R\        R\        R\        R,          R\        /# )r   rQ   rA   rR   effective_roleNr   )rS   rC   r   r(   )r   s   "r   r   r      s8     4 44"4034EH4Z4	4r   c                (   ^ RI HpHp \        T;'       g    VP	                  R4      ;'       g    R4      p\        VP	                  R4      ;'       g    R4      P                  4       P                  4       p\        T;'       g    R4      P                  4       P                  4       p \        R,          P                  4       ;_uu_ 4       p	V	P                  RV .4       V	P                  4       '       d    RRR4       R#  RRR4       T! T 4      w  p
  p \        R,          P                  4       ;_uu_ 4       p	T	P                  R	T! T
4       R
2T .4       T	P                  4       '       g    RRR4       R# T\        9   d    RRR4       R# T	P                  RT! T
4       R2YT.4       T	P                  4       RJuuRRR4       #   + '       g   i     L; i  \         d     Li ; i  + '       g   i     R# ; i  \         d     R# i ; i)a  
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.
)_table_names_q_identrF   r7   rU   rX   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_tablesrx   ry   r>   rG   r   r:   r;   r   rZ   r[   r\   r   rN   )rQ   rA   rR   rv   rx   ry   
role_lowerr]   r^   r`   agents_tabler9   s   &&&&        r   _cluster_acknowledges_businessr}      s    C'(R(R&**V:L(R(RPRSJVZZ(..B/557==?JZ%%2&,,.446J	#**,,KKQ ||~~ -,
  - )5L!Q#**,,KK ,!7 88WX <<>> -, \) -, KK'56 7 *5 <<>-+ -, -,  
 -,,,  s~   )#G +G
7G G #H ;5G.0H :G.H /G.?
H 
G	G G G+*G+.G?	9H ?H HHc                0    V ^8  d   QhR\         R\        /# )r   messagerh   )r   rS   )r   s   "r   r   r      s       c r   c                 &    \        R V .RV /VR7      # )non_field_errorsr   rh   r   )r   rh   s   &&r   _auth_errorr      s     	gY	7; r   POSTc                	   V P                   P                  R4      ;'       g&    V P                   P                  R4      ;'       g    RP                  4       pV P                   P                  R4      ;'       g    RpV P                   P                  R4      ;'       g    RP                  4       p\        P	                  RVV\        V4      4       V'       d	   V'       g   \        R4      #  \        V4      pT'       g"   \        P	                  RT4       \        R4      # TP                  R4      ;'       g    RP                  4       p\        P	                  RT\        T;'       g    R4      TR,          4       T'       d   \        Y%4      '       gB   \        P	                  RTTP                  R4      TP                  R4      4       \        R4      # TP                  R4      pTf1   \        P	                  RYP                  R4      4       \        R4      #  \        T4      p\        YtT4      p\        YT4      p	\        YtYR7      '       g3   \        P	                  RTTP                  R4      T4       \        R4      # \!        4       p
\#        TP                  R4      ;'       g    TP                  R4      ;'       g    T4      R,          pT
P$                  P'                  TR7      w  rT'       d   TP)                  4        \#        TP                  R4      ;'       g    R4      Tn        \-        T	4      pYn        Yn        TP3                  . R#OT'       g   . MR.,           R7       \4        P$                  P'                  TR7      w  pp\7        RTP8                  RR\#        TP                  R4      ;'       g    TP:                  4      R\#        TP                  R4      ;'       g    TP                  R4      ;'       g    R4      R\#        TP                  R4      ;'       g    R4      RT	R TP                  R!4      RTR". //4      #   \         d    \        RR	R
7      u # i ; i  \         d    \        R4      u # i ; i)$z
Custom token endpoint compatible with your React UI + legacy MySQL tables.
Body: { "username": "...", "password": "...", "role": "admin|agent" }
Returns: { "token": "...", "user": {...} }
usernamerU   r7   passwordrF   z8auth.token attempt identifier=%s role=%s has_password=%szMissing username or password.zLegacy database is not reachable from this machine. Update DATABASE_MASTER_URL/DATABASE_CLUSTER_URL in backend/.env or connect to the network/VPN.i  r   z: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:N   Nz=auth.token password mismatch identifier=%s user_id=%s role=%srW   rQ   z7auth.token missing business_id identifier=%s user_id=%szUser is missing business_id.zInvalid business_id.)rv   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.:N   N)r   )update_fields)usertokenr   idnameagentIdrV   permissions)rU   is_staffis_superuser)rq   rG   r:   r.   r/   r(   r   rt   r   lenr4   rS   r-   rd   rI   r}   r   r   objectsget_or_createset_unusable_passwordrU   rO   r   r   saver   r   keyr   )requestrR   r   rF   rA   r&   rQ   business_id_intr@   r{   
DjangoUserdjango_usernamedjango_usercreated
privilegedr   r9   s   &                r   legacy_tokenr      s    ,,"":.QQ',,2B2B72KQQrXXZJ||
+11rHLLV$**113D
NNBX	 X:;;
#J/ SU_`HII::j)//R668K
NNRHNNC	 4XKKKJJy!JJv		
 HII**]+KPR\^h^hir^st9::3k* 7PZ[L'dCJ)  	UJJy!		
 `
 	

  !J&**Z0UUFJJw4GUU:VW[\O%--;;_;UK))+FJJw/5526K&z2J%)#HV]Bdnco#pq}}***<HE1UYYc&**Y/AA;>>BFJJv.NN&**Z2HNNBOVZZ066B7
6::j1r	
 I  
 a
 	

B  31223s$   ,Q Q- Q*)Q*-RRGETc                    V P                   p\        VR R4      '       g   \        VRR4      '       d   RMRp\        VRR4      ;'       g    \        VRR4      ;'       g    RP                  4       pV'       d   \        V4      MRpRpRpRpTpV'       db   VP	                  R	4      pVP	                  R
4      pVP	                  R4      pVe    \        V4      pVe   \        WTV4      MRp	\        WR4      p\        R\        T;'       g    \        VRR4      4      R\        VRR4      R\        VRR4      R\        VRR 4      ! 4       ;'       g    \        VRR4      RVRVR	VR. /4      #   \         d    Rp Li ; i)r   Fr   adminrE   rU   Nr   r7   rQ   rV   rW   r   r   get_full_namec                      R # )r7   r   r   r   r   <lambda>me.<locals>.<lambda>v  s    r   rF   r   r   )r   getattrr:   rt   rG   rS   r-   rd   rI   r   r   )
r   udj_roleidentrA   rQ   rV   legacy_user_idrole_outbias
   &         r   mer   W  s    	A!!Z7771nV[;\;\gcjGQ&LL'!Z*FLL"SSUE*/&TFKHNHjj/::j)I."#!+.
 & 0UK 	
 *#r:#n<<4(<=:r2WQ,WQ<>\\'!ZY[B\Hx;2		
   #"#s   E, ,E<;E<c                   V P                   p\        VRR4      ;'       g    \        VRR4      ;'       g    RP                  4       pV'       d   \        V4      MRpV'       d   VP	                  R4      MRp Ve   \        V4      MRpV'       g   \        RR/RR	7      # \        R
,          P                  4       ;_uu_ 4       p VP                  RV.4       VP                  4       pV'       g   \        RR/RR	7      uuRRR4       # \        RV^ ,          RV^,          RV^,          RV^,          /4      uuRRR4       #   \         d    Rp Li ; i  \         d|    TP                  RT.4       TP                  4       pT'       g   \        RR/RR	7      u uuRRR4       # \        RT^ ,          RT^,          RT^,          RR/4      u uuRRR4       # i ; i  + '       g   i     R# ; i)z
Return current user's business record fields needed by the dashboard.
Source of truth: legacy master DB table `businesses`, column `plans`.
rU   Nr   r7   rQ   r   z$Business not found for current user.i  r   rg   zdSELECT business_id, business_name, plans, ch_language FROM businesses WHERE business_id = %s LIMIT 1zBusiness not found.business_nameplansch_languagezWSELECT business_id, business_name, plans FROM businesses WHERE business_id = %s LIMIT 1)r   r   r:   rt   rG   rS   r-   r   r   rZ   r[   r\   r   )r   r   r   rA   rQ   r   r`   rc   s   &       r   business_planr     s    	AQ&LL'!Z*FLL"SSUE*/&TF/5&**]+4K.9.E#k*4 $JKTWXX 
Y		&	&	(	(C 	KKv ! ,,.C,A B3O 
)	( !3q6#SVSV!3q6	 
)	(  2  	KKi ! ,,.C,A B3OO3 
)	(4 !3q6#SVSV!4	 5 
)	($	% 
)	(	(sT   8E G9E	.EEE5GGG&(GGGGG/	>   r   owner
superadminsuper_adminrM   )i  )&django.contrib.authr   django.contrib.auth.hashersr   r2   	django.dbr   r   loggingrest_framework.authtoken.modelsr   rest_framework.decoratorsr   r	   rest_framework.permissionsr
   r   rest_framework.responser   	getLoggerr   r.   RuntimeErrorr   r#   r4   	frozensetrN   r>   rI   rO   rd   rt   r}   r   r   r   r   r   r   r   <module>r      s    . O 0  1 B @ , 
		8	$	, 	=, HI;%=@D4n 
6(XJg   gT 
5'_%&# ' #L 
5'_%&3 ' 3r   