o
    l‰ÃiL  ã                   @   sˆ   d dl Z d dlZd dlZd dlmZmZ d dlmZ d dlmZm	Z	 d dl
Z
d dlZe
 e¡ZG dd„ dƒZdd„ Zd	d
„ Zdd„ ZdS )é    N)ÚdatetimeÚ	timedelta©Úwraps)ÚrequestÚjsonifyc                   @   s*  e Zd Zdd„ Zdd„ Zedd„ ƒZdd„ Zd	d
„ Zdd„ Z	edd„ ƒZ
dd„ ZdEdd„ZdFdd„Zdd„ ZdGdd„ZdHdd„Zdd„ ZdId d!„Zd"d#„ Zd$d%„ Zd&d'„ Zd(d)„ Zd*d+„ Zd,d-„ ZdJd.d/„Zd0d1„ ZdKd3d4„Zd5d6„ Zed7d8„ ƒZdId9d:„ZdJd;d<„Zd=d>„ Z d?d@„ Z!dAdB„ Z"dCdD„ Z#dS )LÚAuthHandlerc              
   C   sž   || _ | d¡t| dd¡ƒ| d¡| d¡| d¡tjjdœ| _| dd	¡| _d
| _d| _	z|  
¡  W d S  tyN } zt d|¡ W Y d }~d S d }~ww )NÚDB_HOSTÚDB_PORTiê  ÚDB_USERÚDB_PASSWORDÚDB_NAME)ÚhostÚportÚuserÚpasswordÚdatabaseÚcursorclassÚ
SECRET_KEYz)your-secret-key-here-change-in-productionÚHS256é   zDCould not ensure embed tables at startup (DB may be unreachable): %s)ÚconfigÚgetÚintÚpymysqlÚcursorsÚ
DictCursorÚ	db_configÚ
jwt_secretÚjwt_algorithmÚjwt_expiration_daysÚensure_embed_tablesÚ	ExceptionÚloggerÚwarning)Úselfr   Úe© r'   ú)/var/www/html/pca-backend/auth_handler.pyÚ__init__   s"   ú	€ÿzAuthHandler.__init__c                 C   s   t jdi | j¤ŽS )zGet database connectionNr'   )r   Úconnectr   )r%   r'   r'   r(   Úget_connection!   s   zAuthHandler.get_connectionc                 C   s   d|   dd¡› dS )Nú`z``)Úreplace)Ú
identifierr'   r'   r(   Ú_quote_identifier%   s   zAuthHandler._quote_identifierc                 C   s   |  d||f¡ | ¡ d uS )NzSELECT 1
            FROM information_schema.tables
            WHERE table_schema = %s AND table_name = %s
            LIMIT 1©ÚexecuteÚfetchone)r%   ÚcursorÚschemaÚ
table_namer'   r'   r(   Ú_table_exists)   s
   ûzAuthHandler._table_existsc                 C   s\   d|› }|  d||f¡ | ¡ }|r|d S |  d|d|› f¡ | ¡ }|r,|d S d S )NÚ7987_zSELECT table_name AS name
            FROM information_schema.tables
            WHERE table_schema = %s AND table_name = %s
            LIMIT 1Únamez³SELECT table_name AS name
            FROM information_schema.tables
            WHERE table_schema = %s AND table_name LIKE %s
            ORDER BY table_name
            LIMIT 1z%_r0   )r%   r3   r4   ÚsuffixÚ	preferredÚrowr'   r'   r(   Ú_find_template_table3   s   
ûúz AuthHandler._find_template_tablec                 C   s    t  ¡ }t  | d¡|¡ d¡S )zHash a password using bcryptúutf-8)ÚbcryptÚgensaltÚhashpwÚencodeÚdecode)r%   r   Úsaltr'   r'   r(   Úhash_passwordK   s   zAuthHandler.hash_passwordc                 C   s,   | rt | tƒs	dS |  d¡sdS t| ƒdkS )NF)z$2a$z$2b$z$2y$é2   )Ú
isinstanceÚstrÚ
startswithÚlen)Úpassword_hashr'   r'   r(   Ú_looks_like_bcrypt_hashP   s
   
z#AuthHandler._looks_like_bcrypt_hashc                 C   s>   |   |¡sdS zt | d¡| d¡¡W S  ty   Y dS w )z"Verify a password against its hashFr=   )rK   r>   ÚcheckpwrA   Ú
ValueError)r%   r   rJ   r'   r'   r(   Úverify_passwordY   s   
þzAuthHandler.verify_passwordNc           
   
   C   s–   |   ¡ }zAz| ¡ }| d||||||f¡ | ¡  W n ty7 }	 zt dt|	ƒ› ¡ W Y d}	~	nd}	~	ww W | ¡  dS W | ¡  dS | ¡  w )zLog user activityz¥INSERT INTO user_activity_log
                (user_id, username, activity_type, description, ip_address, user_agent)
                VALUES (%s, %s, %s, %s, %s, %s)zError logging activity: N)	r+   r3   r1   Úcommitr"   r#   ÚerrorrG   Úclose)
r%   Úuser_idÚusernameÚactivity_typeÚdescriptionÚ
ip_addressÚ
user_agentÚconnr3   r&   r'   r'   r(   Úlog_activityc   s    ü €ÿÿþzAuthHandler.log_activityFc              	   C   s@   |||||t  ¡ t| jd t  ¡ dœ}tj|| j| jd}|S )z=Generate a JWT token with user info and accessible businesses)Údays)rR   rS   ÚemailÚ
businessesÚ	is_masterÚexpÚiat©Ú	algorithm)r   Úutcnowr   r    ÚjwtrA   r   r   )r%   rR   rS   r[   r\   r]   ÚpayloadÚtokenr'   r'   r(   Úgenerate_jwt_tokent   s   ù	zAuthHandler.generate_jwt_tokenc                 C   sH   zt j|| j| jgd}|W S  t jy   Y dS  t jy#   Y dS w )zDecode and validate a JWT token©Ú
algorithmsN)rc   rB   r   r   ÚExpiredSignatureErrorÚInvalidTokenError©r%   re   rd   r'   r'   r(   Údecode_jwt_token‚   s   ÿzAuthHandler.decode_jwt_tokenr   c              
   C   s.  |   ¡ }zz_| ¡ }t|pdƒ ¡  ¡ }|dvr$ddidfW W | ¡  S | d||f¡ | ¡ r<ddidfW W | ¡  S |  |¡}	| d	|||	||||f¡ | 	¡  |j
}
|
||||d
œdfW W | ¡  S  ty‘ } z!| ¡  t dt|ƒ› ¡ dt|ƒidfW  Y d}~W | ¡  S d}~ww | ¡  w )z/Create a new user (no business assignment here)r   ©r   ÚadminrP   ú Invalid role. Use user or admin.é  z?SELECT id FROM business_users WHERE username = %s OR email = %sz Username or email already existsé™  z¼INSERT INTO business_users
                (username, email, password_hash, plain_password, full_name, role, is_master, is_active)
                VALUES (%s, %s, %s, %s, %s, %s, %s, TRUE))ÚidrS   r[   Úroler]   éÉ   zError creating user: éô  N)r+   r3   rG   ÚstripÚlowerrQ   r1   r2   rD   rO   Ú	lastrowidr"   Úrollbackr#   rP   )r%   rS   r[   r   Ú	full_namers   r]   rX   r3   rJ   rR   r&   r'   r'   r(   Úcreate_userŒ   sL   
%Þþ

æüûú
û
€û
zAuthHandler.create_userc              
   C   sB  |   ¡ }z—zi| ¡ }t|pdƒ ¡  ¡ }|dvr$ddidfW W | ¡  S | d|f¡ | ¡ s;ddidfW W | ¡  S | d	|f¡ | ¡ sRdd
idfW W | ¡  S | d||||f¡ | ¡  d|||dœdfW W | ¡  S  t	y› } z!| 
¡  t dt|ƒ› ¡ dt|ƒidfW  Y d}~W | ¡  S d}~ww | ¡  w )z Assign business access to a userr   rm   rP   ro   rp   ú)SELECT bid FROM businesses WHERE bid = %súBusiness not foundé”  z+SELECT id FROM business_users WHERE id = %súUser not foundz‹INSERT INTO user_business_access (user_id, bid, role)
                VALUES (%s, %s, %s)
                ON DUPLICATE KEY UPDATE role = %szBusiness access granted)ÚmessagerR   Úbidrs   éÈ   z!Error assigning business access: ru   N)r+   r3   rG   rv   rw   rQ   r1   r2   rO   r"   ry   r#   rP   )r%   rR   r   rs   rX   r3   r&   r'   r'   r(   Úassign_business_access»   s<   
ç
ì
ñ
ü
û
€û
z"AuthHandler.assign_business_accessc              
   C   sŠ   |   ¡ }z;z| ¡ }| d|f¡ | ¡ }|W W | ¡  S  ty? } zt dt|ƒ› ¡ g W  Y d}~W | ¡  S d}~ww | ¡  w )z'Get all businesses accessible to a userzîSELECT b.bid, b.name, b.description, uba.role
                FROM user_business_access uba
                JOIN businesses b ON uba.bid = b.bid
                WHERE uba.user_id = %s AND b.is_active = TRUE
                ORDER BY b.namezError getting user businesses: N©	r+   r3   r1   ÚfetchallrQ   r"   r#   rP   rG   )r%   rR   rX   r3   r\   r&   r'   r'   r(   Úget_user_businessesá   s"   ú
ü
€ü
zAuthHandler.get_user_businessesc              
   C   s*  |   ¡ }z
zÛ| ¡ }| d||f¡ | ¡ }|s%ddidfW W | ¡  S |  || d¡¡}d}	|sR| d¡}
|
rB||
krBd}d}	n| d¡rR|| d¡krRd}d}	|s`ddidfW W | ¡  S |	ry|  |¡}| d	||d
 f¡ t 	d|d ¡ |  
|d
 ¡}|d r| d¡ | ¡ }| d|d
 f¡ | ¡  | j|d
 |d |d ||d d}| j|d
 |d dd|d › d||d ||d
 |d |d |d |d |d |dœdœdfW W | ¡  S  ty } z!| ¡  t dt|ƒ› ¡ dt|ƒidfW  Y d}~W | ¡  S d}~ww | ¡  w )z&Authenticate user and create JWT tokenzÝSELECT id, username, email, password_hash, plain_password, full_name,
                role, is_master, is_active
                FROM business_users
                WHERE (username = %s OR email = %s) AND is_active = TRUErP   zInvalid credentialsé‘  rJ   FÚplain_passwordTz:UPDATE business_users SET password_hash = %s WHERE id = %srr   z=Upgraded password hash for user %s due to legacy/invalid hashrS   r]   zŸSELECT bid, name, description, 'admin' as role
                    FROM businesses
                    WHERE is_active = TRUE
                    ORDER BY namez:UPDATE business_users SET last_login = NOW() WHERE id = %sr[   )rR   rS   r[   r\   r]   ÚloginzUser z logged in successfully)rR   rS   rT   rU   rV   rW   rz   rs   )rr   rS   r[   rz   rs   r]   r\   )re   r   r‚   zError during login: ru   N)r+   r3   r1   r2   rQ   rN   r   rD   r#   r$   r†   r…   rO   rf   rY   r"   ry   rP   rG   )r%   rS   r   rV   rW   rX   r3   r   Úpassword_okÚneeds_upgraderˆ   Únew_hashr\   re   r&   r'   r'   r(   r‰   ø   s¢   û
W¬

F¼

þþÿþû	ú
ùþõ
û
€û
zAuthHandler.loginc                 C   sD   |   |¡}|s	dS | d¡| d¡| d¡| dg ¡| dd¡dœS )	z)Validate a JWT token and return user infoNrR   rS   r[   r\   r]   F)rr   rS   r[   r\   r]   )rl   r   rk   r'   r'   r(   Úvalidate_tokenb  s   


ûzAuthHandler.validate_tokenc                 C   s   ddidfS )z8Logout user (with JWT, we just rely on token expiration)r€   zLogged out successfullyr‚   r'   )r%   re   r'   r'   r(   Úlogoutp  s   zAuthHandler.logoutc              
   C   sN  |rt |ƒdk rddidfS |  ¡ }zza| ¡ }| d|f¡ | ¡ }|s1ddidfW W | ¡  S |  || d¡¡}|sI| d	¡}|rI||krId
}|sWddidfW W | ¡  S |  |¡}	| d|	||f¡ | 	¡  ddidfW W | ¡  S  t
y¡ }
 z!| ¡  t dt|
ƒ› ¡ dt|
ƒidfW  Y d}
~
W | ¡  S d}
~
ww | ¡  w )z8Change a user's password after verifying the current oneé   rP   z*New password must be at least 6 charactersrp   z[SELECT password_hash, plain_password FROM business_users WHERE id = %s AND is_active = TRUEr   r~   rJ   rˆ   TzCurrent password is incorrectr‡   zOUPDATE business_users SET password_hash = %s, plain_password = %s WHERE id = %sr€   zPassword changed successfullyr‚   zError changing password: ru   N)rI   r+   r3   r1   r2   rQ   rN   r   rD   rO   r"   ry   r#   rP   rG   )r%   rR   Úcurrent_passwordÚnew_passwordrX   r3   r   rŠ   ÚplainrŒ   r&   r'   r'   r(   Úchange_passwordv  sJ   þ
ì


ôþ
û
€û
zAuthHandler.change_passwordc              
   C   sÊ   |   ¡ }z[z-| ¡ }| d||f¡ | ¡  |jdkr'ddidfW W | ¡  S ddidfW W | ¡  S  ty_ } z!| ¡  t 	d	t
|ƒ› ¡ dt
|ƒid
fW  Y d}~W | ¡  S d}~ww | ¡  w )zUpdate a user's profile detailszKUPDATE business_users SET full_name = %s WHERE id = %s AND is_active = TRUEr   rP   r   r~   r€   zProfile updated successfullyr‚   zError updating profile: ru   N©r+   r3   r1   rO   ÚrowcountrQ   r"   ry   r#   rP   rG   )r%   rR   rz   rX   r3   r&   r'   r'   r(   Úupdate_profile›  s*   þ

ú
û
€û
zAuthHandler.update_profilec              
   C   s²   |   ¡ }zOz%| ¡ }| d¡ | ¡ }|D ]}|  |d ¡|d< q|dfW W | ¡  S  tyS } zt dt	|ƒ› ¡ dt	|ƒidfW  Y d}~W | ¡  S d}~ww | ¡  w )	z!Get all users (master admin only)zÆSELECT id, username, email, plain_password, full_name, role, is_master, is_active,
                created_at, last_login
                FROM business_users
                ORDER BY created_at DESCrr   r\   r‚   zError getting users: rP   ru   N)
r+   r3   r1   r…   r†   rQ   r"   r#   rP   rG   )r%   rX   r3   Úusersr   r&   r'   r'   r(   Úget_all_users¯  s$   ÿ

ü
€ü
zAuthHandler.get_all_usersc           
      C   s,  |   ¡ }zŒzb| ¡ }| dt|ƒf¡ | ¡ }g }|D ]B}| d¡p#d ¡ }| d¡p,d ¡ }| | d¡|| d¡||p>|| d¡pI| d¡pId	| d
¡| d¡| d¡rXdnddœ	¡ q|dfW W | ¡  S  t	y }	 zt
 dt|	ƒ› ¡ dt|	ƒidfW  Y d}	~	W | ¡  S d}	~	ww | ¡  w )z0Get users assigned to a specific business (bid).a  
                SELECT
                    bu.id,
                    bu.username,
                    bu.email,
                    bu.full_name,
                    bu.role,
                    bu.is_master,
                    bu.is_active,
                    uba.role AS business_role
                FROM user_business_access uba
                JOIN business_users bu ON bu.id = uba.user_id
                WHERE uba.bid = %s
                  AND bu.is_active = TRUE
                ORDER BY bu.created_at DESC
                rz   Ú rS   rr   r[   Úbusiness_rolers   r   r]   Ú	is_activeÚActiveÚInactive)	rr   rS   r[   rz   r8   rs   r]   r›   Ústatusr‚   z!Error getting users by business: rP   ru   N)r+   r3   r1   rG   r…   r   rv   ÚappendrQ   r"   r#   rP   )
r%   r   rX   r3   r—   Ú
normalizedÚurz   rS   r&   r'   r'   r(   Úget_users_by_businessÇ  s@   ï
ö

ü
€ü
z!AuthHandler.get_users_by_businessc                 C   s„  t |ƒ}|  ¡ }g }z1z¯| ¡ }| d|f¡ | ¡ r(ddidfW W | ¡  S | jd }g d¢}i }	|D ] }
|  |||
¡}|sQdd|
› idf  W W | ¡  S ||	|
< q5|D ]"}
|› d	|
› }|  |||¡rzdd
|› didf  W W | ¡  S qX|D ]#}
|› d	|
› }|	|
 }| d|  	|¡› d|  	|¡› ¡ | 
|¡ q}| d|||f¡ | ¡  |||dœdfW W | ¡  S  tjyÔ   | ¡  ddidf Y W | ¡  S  ty< } z\| ¡  t dt |ƒ› ¡ |r%z| ¡ }|D ]}| d|  	|¡› ¡ qô| ¡  W n ty$ } zt dt |ƒ› ¡ W Y d}~nd}~ww dt |ƒidfW  Y d}~W | ¡  S d}~ww | ¡  w )z)Create a new business (master admin only)r|   rP   zBusiness ID already existsrq   r   )Ú	raw_callsÚsarvamresponseÚcallanalyticszNo template table found for ru   Ú_zTable z already existszCREATE TABLE z LIKE zdINSERT INTO businesses (bid, name, description, is_active)
                VALUES (%s, %s, %s, TRUE))r   r8   rU   rt   zError creating business: zDROP TABLE IF EXISTS zError cleaning up tables: N)rG   r+   r3   r1   r2   rQ   r   r<   r6   r/   rŸ   rO   r   ÚIntegrityErrorry   r"   r#   rP   )r%   r   r8   rU   rX   Úcreated_tablesr3   r4   ÚsuffixesÚ	templatesr9   Útemplater5   r&   Úcleanup_cursorÚcleanup_errorr'   r'   r(   Úcreate_businessú  sŠ   
9
É
0
Ñ
*Õÿÿýýü
î
ñÿ €ÿ
€ñ
zAuthHandler.create_businessc              
   C   s–   |   ¡ }zAz| ¡ }| d¡ | ¡ }|dfW W | ¡  S  tyE } zt dt|ƒ› ¡ dt|ƒidfW  Y d}~W | ¡  S d}~ww | ¡  w )zGet all businesseszrSELECT bid, name, description, is_active, created_at
                FROM businesses
                ORDER BY namer‚   zError getting businesses: rP   ru   Nr„   )r%   rX   r3   r\   r&   r'   r'   r(   Úget_all_businesses@  s    ÿ

ü
€ü
zAuthHandler.get_all_businesseséd   c           
   
   C   sÞ   |   ¡ }zez;| ¡ }d}g }|r|d7 }| |¡ |r$|d7 }| |¡ |d7 }| |¡ | ||¡ | ¡ }|dfW W | ¡  S  tyi }	 zt dt	|	ƒ› ¡ dt	|	ƒidfW  Y d	}	~	W | ¡  S d	}	~	ww | ¡  w )
zGet activity log entriesaA  SELECT al.id, al.user_id, al.username, al.activity_type, 
                       al.description, al.ip_address, al.user_agent, al.created_at,
                       bu.email, bu.full_name
                FROM user_activity_log al
                LEFT JOIN business_users bu ON al.user_id = bu.id
                WHERE 1=1z AND al.user_id = %sz AND al.activity_type = %sz% ORDER BY al.created_at DESC LIMIT %sr‚   zError getting activity log: rP   ru   N)
r+   r3   rŸ   r1   r…   rQ   r"   r#   rP   rG   )
r%   ÚlimitrR   rT   rX   r3   ÚqueryÚparamsÚlogsr&   r'   r'   r(   Úget_activity_logR  s0   




ü
€ü
zAuthHandler.get_activity_logc              
   C   s’   |   ¡ }z?z| ¡ }| d¡ | ¡  t d¡ W n ty5 } zt dt|ƒ› ¡ W Y d}~nd}~ww W | 	¡  dS W | 	¡  dS | 	¡  w )z/Create embed_api_keys table if it doesn't existaô  
                CREATE TABLE IF NOT EXISTS embed_api_keys (
                    id INT AUTO_INCREMENT PRIMARY KEY,
                    api_key VARCHAR(64) UNIQUE NOT NULL,
                    bid VARCHAR(20) NOT NULL,
                    partner_name VARCHAR(100) NOT NULL,
                    allowed_origins TEXT DEFAULT NULL,
                    is_active TINYINT(1) DEFAULT 1,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    expires_at TIMESTAMP NULL DEFAULT NULL,
                    last_used_at TIMESTAMP NULL DEFAULT NULL,
                    INDEX idx_api_key (api_key),
                    INDEX idx_bid (bid)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
            zembed_api_keys table ensuredzError ensuring embed tables: N)
r+   r3   r1   rO   r#   Úinfor"   rP   rG   rQ   )r%   rX   r3   r&   r'   r'   r(   r!   z  s   
 €ÿÿþzAuthHandler.ensure_embed_tablesc                   C   s   dt  d¡ S )z*Generate a unique API key for embed accessÚ	sk_embed_é   )ÚsecretsÚ	token_hexr'   r'   r'   r(   Úgenerate_api_key•  s   zAuthHandler.generate_api_keyc           	   
   C   sü   |   ¡ }ztzF| ¡ }| d|f¡ | ¡ s!ddidfW W | ¡  S |  ¡ }| d|||||f¡ | ¡  |j|||||r@t|ƒnddœdfW W | ¡  S  t	yx } z!| 
¡  t d	t|ƒ› ¡ dt|ƒid
fW  Y d}~W | ¡  S d}~ww | ¡  w )z)Create a new embed API key for a businessr|   rP   r}   r~   zINSERT INTO embed_api_keys
                (api_key, bid, partner_name, allowed_origins, expires_at)
                VALUES (%s, %s, %s, %s, %s)N)rr   Úapi_keyr   Úpartner_nameÚallowed_originsÚ
expires_atrt   zError creating embed API key: ru   )r+   r3   r1   r2   rQ   r»   rO   rx   rG   r"   ry   r#   rP   )	r%   r   r½   r¾   r¿   rX   r3   r¼   r&   r'   r'   r(   Úcreate_embed_api_keyš  s>   
èüúù
û
€û
z AuthHandler.create_embed_api_keyc              
   C   sÖ   |   ¡ }zaz7| ¡ }|r| d|f¡ n| d¡ | ¡ }|D ]}|d }d|dd…  |d< |d= q|dfW W | ¡  S  tye } zt d	t|ƒ› ¡ d
t|ƒidfW  Y d}~W | ¡  S d}~ww | ¡  w )z3List all embed API keys, optionally filtered by bida‡  SELECT ek.id, ek.api_key, ek.bid, b.name as business_name,
                       ek.partner_name, ek.allowed_origins, ek.is_active,
                       ek.created_at, ek.expires_at, ek.last_used_at
                    FROM embed_api_keys ek
                    LEFT JOIN businesses b ON ek.bid = b.bid
                    WHERE ek.bid = %s
                    ORDER BY ek.created_at DESCaa  SELECT ek.id, ek.api_key, ek.bid, b.name as business_name,
                       ek.partner_name, ek.allowed_origins, ek.is_active,
                       ek.created_at, ek.expires_at, ek.last_used_at
                    FROM embed_api_keys ek
                    LEFT JOIN businesses b ON ek.bid = b.bid
                    ORDER BY ek.created_at DESCr¼   z***iøÿÿÿNÚapi_key_maskedr‚   zError listing embed API keys: rP   ru   r„   )r%   r   rX   r3   ÚkeysÚkeyÚfull_keyr&   r'   r'   r(   Úlist_embed_api_keys¿  s2   øÿ	

ü
€ü
zAuthHandler.list_embed_api_keysc              
   C   sÈ   |   ¡ }zZz,| ¡ }| d|f¡ | ¡  |jdkr&ddidfW W | ¡  S ddidfW W | ¡  S  ty^ } z!| ¡  t 	d	t
|ƒ› ¡ dt
|ƒid
fW  Y d}~W | ¡  S d}~ww | ¡  w )z$Revoke (deactivate) an embed API keyz5UPDATE embed_api_keys SET is_active = 0 WHERE id = %sr   rP   zAPI key not foundr~   r€   zAPI key revoked successfullyr‚   zError revoking embed API key: ru   Nr”   )r%   Úkey_idrX   r3   r&   r'   r'   r(   Úrevoke_embed_api_keyê  s*   þ

	ù
û
€û
z AuthHandler.revoke_embed_api_keyc              
   C   sà   |   ¡ }zfzC| ¡ }| d||f¡ | ¡ }|s W W | ¡  dS |d r4|d t ¡ k r4W W | ¡  dS | d|d f¡ | ¡  |W W | ¡  S  tyj } zt	 
dt|ƒ› ¡ W Y d}~W | ¡  dS d}~ww | ¡  w )z1Validate an embed API key for a specific businessz¨SELECT id, api_key, bid, partner_name, allowed_origins, expires_at
                FROM embed_api_keys
                WHERE api_key = %s AND bid = %s AND is_active = 1Nr¿   z<UPDATE embed_api_keys SET last_used_at = NOW() WHERE id = %srr   zError validating API key: )r+   r3   r1   r2   rQ   r   rb   rO   r"   r#   rP   rG   )r%   r¼   r   rX   r3   Ú
key_recordr&   r'   r'   r(   Úvalidate_api_key  s8   üðôþ
ü€ü
zAuthHandler.validate_api_keyc                 C   s@   dt |ƒ||t ¡ tdd t ¡ dœ}tj|| j| jd}|S )z5Generate a short-lived JWT token for iframe embeddingÚembedé   )Úhours)Útyper   r½   Ú
api_key_idr^   r_   r`   )rG   r   rb   r   rc   rA   r   r   )r%   r   r½   rÎ   rd   re   r'   r'   r(   Úgenerate_embed_token&  s   úz AuthHandler.generate_embed_tokenc                 C   sŠ   z%t j|| j| jgd}| d¡dkrW dS | d¡| d¡| d¡dœW S  t jy5   t d	¡ Y dS  t jyD   t d
¡ Y dS w )zValidate an embed JWT tokenrg   rÍ   rÊ   Nr   r½   rÎ   )r   r½   rÎ   zEmbed token expiredzInvalid embed token)	rc   rB   r   r   r   ri   r#   r$   rj   rk   r'   r'   r(   Úvalidate_embed_token3  s   ý

þz AuthHandler.validate_embed_token)NNN)F)Nr   F)r   )NN)N)r°   NN)$Ú__name__Ú
__module__Ú__qualname__r)   r+   Ústaticmethodr/   r6   r<   rD   rK   rN   rY   rf   rl   r{   rƒ   r†   r‰   r   rŽ   r“   r–   r˜   r¢   r®   r¯   rµ   r!   r»   rÀ   rÅ   rÇ   rÉ   rÏ   rÐ   r'   r'   r'   r(   r      sH    








/&
j%
3F
(


%+$r   c                    ó   t ˆ ƒ‡ fdd„ƒ}|S )z.Decorator to require authentication for routesc                     sz   t j d¡}|r| d¡stddiƒdfS | dd¡}ddlm} |j}| 	|¡}|s3tdd	iƒdfS |t _
ˆ | i |¤ŽS )
NÚAuthorizationzBearer rP   z'Missing or invalid authorization headerr‡   r™   r   )Úcurrent_appzInvalid or expired token)r   Úheadersr   rH   r   r-   Úflaskr×   Úauth_handlerr   Úcurrent_user)ÚargsÚkwargsÚauth_headerre   r×   rÚ   r   ©Úfr'   r(   Údecorated_functionJ  s   
z(require_auth.<locals>.decorated_functionr   ©rà   rá   r'   rß   r(   Úrequire_authH  s   rã   c                    rÕ   )z(Decorator to require master admin accessc                     s.   t j}| d¡stddiƒdfS ˆ | i |¤ŽS )Nr]   rP   zMaster admin access requiredé“  )r   rÛ   r   r   )rÜ   rÝ   r   rß   r'   r(   rá   e  s   
z*require_master.<locals>.decorated_functionr   râ   r'   rß   r(   Úrequire_masterc  s   rå   c                    rÕ   )z=Decorator to ensure user has access to the requested businessc                     sp   |  d¡p
tj  d¡}tj}|  d¡rˆ | i |¤ŽS dd„ |  dg ¡D ƒ}||vr1tddiƒdfS ˆ | i |¤ŽS )	Nr   r]   c                 S   s   g | ]}|d  ‘qS )r   r'   )Ú.0Úbr'   r'   r(   Ú
<listcomp>{  s    zGrequire_business_access.<locals>.decorated_function.<locals>.<listcomp>r\   rP   zAccess denied to this businessrä   )r   r   Ú	view_argsrÛ   r   )rÜ   rÝ   r   r   Úuser_businessesrß   r'   r(   rá   p  s   
z3require_business_access.<locals>.decorated_functionr   râ   r'   rß   r(   Úrequire_business_accessn  s   rë   )r   r>   rc   r   r   Ú	functoolsr   rÙ   r   r   Úloggingr¹   Ú	getLoggerrÑ   r#   r   rã   rå   rë   r'   r'   r'   r(   Ú<module>   s&    
      A