
    Cj3                     $   U d Z dZddlZddlZddlmZ ddlmZmZ ddlm	Z	 ddl
Z
ddlmZmZmZmZmZ  ej        d          Zdae	e
j                 ed	<   d
 Zd ZdedefdZe G d d                      Z G d d          Z e            ZdS )u	  
License validity check for the params endpoint.

Reads the SAME `licenses` table that /wd/tradesimulator/main.py (the
existing tsvalid service) already validates against — confirmed schema:

    key_hash             char(64)      SHA-256 of the raw license key
    registered_account   varchar(64)   actually holds the machine
                                        fingerprint (repurposed; the name
                                        is misleading but this is what
                                        tsvalid/main.py actually reads/
                                        writes)
    registered_machine   char(64)      CONFIRMED DEAD as of 2026-06-30 —
                                        0 of 22 existing rows have any
                                        value here, no other process on
                                        the box references it. Ignored
                                        entirely, deliberately, do not
                                        wire this in without re-checking
                                        that it's still unused first.
    expires_at           datetime      NULL = never expires
    is_active            tinyint(1)    admin kill-switch

PLUS the new `license_products` join table (migration_license_products.sql)
— a license must have an explicit ('trend_degree') row here, in addition
to passing all the checks above, or this endpoint treats it as INVALID.
Existing licenses were backfilled with 'tradesimulator' only on migration
— nobody gets trend_degree access by default; it's opt-in per license.

STRICTLY READ-ONLY. This module performs SELECT only — never UPDATE,
never INSERT, never touches audit_log. Uses a separate, SELECT-only DB
user (paramsreader) rather than tsvalid's own credentials, so this is
enforced at the database level, not just by this code behaving itself.

DELIBERATELY DOES NOT perform first-activation or machine-transfer
logic — that stays solely tsvalid's job. By the time anything calls the
params endpoint, the EA should have already validated through the real
/tsvalid endpoint first (on init, and hourly via LV_OnTimer), so the
machine should already be registered. This check only confirms "is this
request's machine the one currently on file" — if no machine is
registered yet at all, that's treated as INVALID here (not as an
invitation to register one), since registration is not this service's
responsibility.
trend_degree    N)	dataclass)datetimetimezone)Optional)LICENSE_DB_HOSTLICENSE_DB_PORTLICENSE_DB_USERLICENSE_DB_PASSLICENSE_DB_NAMEparams_license_check_poolc                  ,  K   t           d S t                              dt          t          t
          t                     t          j        t          t          t          t          t
          dddd	  	         d {V a t                              d           d S )Nz,Connecting (read-only) to %s:%s/%s as %s ...utf8mb4T      )	hostportuserpassworddbcharset
autocommitminsizemaxsizez"License DB pool ready (read-only).)
r   loginfor   r	   r   r
   aiomysqlcreate_poolr        %/wd/td-params-server/license_check.py	init_poolr#   A   s      HH;R R R&?        E HH122222r!   c                     K   t           <t                                            t                                            d {V  d a d S d S N)r   closewait_closedr    r!   r"   
close_poolr(   S   sN      !!!!!!!!! r!   raw_keyreturnc                 r    t          j        |                                                                           S r%   )hashlibsha256encode	hexdigest)r)   s    r"   	_key_hashr0   [   s*     >'..**++55777r!   c                   4    e Zd ZU eed<   dZee         ed<   dS )LicenseCheckResultcodeN
license_id)__name__
__module____qualname__str__annotations__r4   r   intr    r!   r"   r2   r2   a   s/         
III $J$$$$$r!   r2   c                   6    e Zd ZdededefdZdededefdZdS )	LicenseStorelicense_key
machine_idr*   c                 P  K   t           *t                              d           t          d          S t	          |          }t                                           4 d {V }|                    t          j                  4 d {V }|	                    d|f           d {V  |
                                 d {V }d d d           d {V  n# 1 d {V swxY w Y   d d d           d {V  n# 1 d {V swxY w Y   |t          d          S |d         st          d          S |d         Nt          j        t          j                                      d           }|d         |k     rt          d	          S |d
         }|t          d|d                   S t!          |          |k    rW|                     |d         t$                     d {V }	|	st          d|d                   S t          d|d                   S t          d|d                   S )Nu?   License DB pool not initialized — call init_pool() at startupINVALID)r3   z
                    SELECT id, registered_account, expires_at, is_active
                    FROM   licenses
                    WHERE  key_hash = %s
                    	is_activeINACTIVE
expires_at)tzinfoEXPIREDregistered_accountid)r3   r4   VALID)r   r   errorr2   r0   acquirecursorr   
DictCursorexecutefetchoner   nowr   utcreplacer8   _has_productREQUIRED_PRODUCT)
selfr=   r>   khconncurrownow_utcregistered_machinehas_entitlements
             r"   checkzLicenseStore.checkh   s[     =IIWXXX%95555{##==?? 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+d{{8#677 	+ 	+ 	+ 	+ 	+ 	+ 	+3kk
 E          LLNN******	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ 
	+ ;%95555; 	7%:6666|(l8<00888EEG< 7**)y9999 !56%
 &9TKKKK!""j00
 %)$5$5c$iAQ$R$RRRRRRRO" P *ySYOOOO%7s4yIIII "ySYGGGGs6   "&C58C C5
C	C5C	 C55
C?C?r4   productc                   K   t                                           4 d {V }|                                4 d {V }|                    d||f           d {V  |                                 d {V d ucd d d           d {V  cd d d           d {V  S # 1 d {V swxY w Y   	 d d d           d {V  d S # 1 d {V swxY w Y   d S )Nz
                    SELECT 1 FROM license_products
                    WHERE license_id = %s AND product = %s
                    LIMIT 1
                    )r   rJ   rK   rM   rN   )rT   r4   r]   rV   rW   s        r"   rR   zLicenseStore._has_product   s;     ==?? 
	8 
	8 
	8 
	8 
	8 
	8 
	8d{{}} 	8 	8 	8 	8 	8 	8 	8kk
  )         !\\^^++++++47	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8 
	8s4   C:B7C
B&	&C)B&	*C
CCN)	r5   r6   r7   r8   r2   r\   r:   boolrR   r    r!   r"   r<   r<   g   so        <Hs <H <H@R <H <H <H <H|8S 83 84 8 8 8 8 8 8r!   r<   )__doc__rS   loggingr,   dataclassesr   r   r   typingr   r   configr   r	   r
   r   r   	getLoggerr   r   Poolr9   r#   r(   r8   r0   r2   r<   license_storer    r!   r"   <module>rh      s  * * *X "    ! ! ! ! ! ! ' ' ' ' ' ' ' '                    
 g.//!%x % % %3 3 3$  8s 8s 8 8 8 8 % % % % % % % %
J8 J8 J8 J8 J8 J8 J8 J8^ r!   