
    eiA+                        d dl m Z mZmZ d dlZd dlZd dlZd dlmZmZ d dl	Z	d dl
mZmZmZmZ d dlmZ d dlmZmZ d dlmZ d dlmZmZmZ d d	lmZmZmZmZmZ d d
l m!Z!m"Z" d dl#m$Z$ d dl%m&Z&m'Z'm(Z( d dl)m*Z*m+Z+ dZ,dZ-dZ.dZ/dZ0dZ1dZ2 e3e/d          5 Z4e45                                Z6ddd           n# 1 swxY w Y    e3dd          5 Z4 e$j7        e45                                d          Z8ddd           n# 1 swxY w Y    e            Z9 ed          Z:ddgZ;e:<                    ee;ddgdg            ede'j=         ed  ed!          d"           ed# ed$          d"                    Z> G d% d&e'          Z? G d' d(e'          Z@e'j=        A                    e()            G d* d+e          ZB G d, d-e          ZC G d. d/e          ZD G d0 d1e          ZEd2 ZFd3e!d4eGd5eGd6eeG         d7e?f
d8ZHd eGd6eeG         d7eGfd9ZIe:J                    d:eCejK        ;           eeF          fd<eBd3e!fd=            ZLe:J                    d>eE?           ee9           eeF          fd<eDd@ed3e!fdA            ZMe:N                    dBeeG         ?           eeF          fd3e!fdC            ZOd3e!d eGdDeGd7eeG         fdEZP	 dXd3e!d eGdDeGdFeGdGeGdHeGdIeQdz  dJeGdz  fdKZRd3e!d eGdDeGdGeeG         d7e+f
dLZSdMe+fdNZTe:N                    dO          dPeQd7eGfdQ            ZUeVdRk    r e&            5 Z%g dSZWeWD ]kZXe%Y                    e@          Z                    eXT          [                                s.e%\                     e@ ej]        dU          eXV                     le%^                                 ddd           n# 1 swxY w Y    e_dW           dS dS )Y    )datetime	timedeltatimezoneN)ListOptional)DependsFastAPIHTTPExceptionstatus)CORSMiddleware)	HTTPBasicHTTPBasicCredentials)jwt)	BaseModelEmailStrconstr)ColumnDateTime
ForeignKeyStringTable)Sessionrelationship)serialization)SessionLocalBaseengine)	EpicTokenEpicAppConfigzsmartstop.io   RS256zkeys/privatekey.pemzjwks_live_patients.jsoni  ,  rrb)passwordu"   Sentinel Connect – Token Service)titlezhttp://localhost:3000zhttp://127.0.0.1:3000T*)allow_originsallow_credentialsallow_methodsallow_headersclient_scope	client_idz
clients.idprimary_keyscope_idz	scopes.idc                      e Zd ZdZ e ed          dd          Z e ed          d          Z e ed          d          Z e ed          d          Z	 ee
 ej                    	          Z ed
ed          ZdS )Clientclients@   T)r/   index   F)nullable   )defaultScope	secondaryback_populatesN)__name__
__module____qualname____tablename__r   r   idsecretorgcontactr   r   now
created_atr   r,   scopes     D/var/www/html/epic_fhir/sentinel-connect/connectors/token_service.pyr2   r2   B   s        M	r

D	9	9	9BVFF3KK%000F
&u
-
-
-CfVVC[[5111G,(,..999J\'\)TTTFFFrJ   r2   c                       e Zd ZdZ e ed          d          Z e ed          dd          Z ede	d          Z
d	S )
r:   rH   r4   Tr.   r6   )uniquer5   r2   r;   N)r>   r?   r@   rA   r   r   rB   namer   r,   r3   rI   rJ   rK   r:   r:   L   sc        M	r

	-	-	-B6&&++d$777Dl8|HUUUGGGrJ   r:   )bindc                   d    e Zd ZU  edd          ed<   eed<   e ed                   ed<   d	S )

RegisterInT   )strip_whitespace
min_lengthorg_namecontact_emailz^[a-z0-9_.]+$)patternrH   N)r>   r?   r@   r   __annotations__r   r   rI   rJ   rK   rQ   rQ   Z   sX         fdq999999 0111222222rJ   rQ   c                   $    e Zd ZU eed<   eed<   dS )RegisterOutr-   client_secretN)r>   r?   r@   strrX   rI   rJ   rK   rZ   rZ   `   s'         NNNrJ   rZ   c                   &    e Zd ZU ee         ed<   dS )TokenInrH   N)r>   r?   r@   r   r\   rX   rI   rJ   rK   r^   r^   e   s"         IrJ   r^   c                   2    e Zd ZU eed<   dZeed<   eed<   dS )TokenOutaccess_tokenBearer
token_type
expires_inN)r>   r?   r@   r\   rX   rc   intrI   rJ   rK   r`   r`   i   s5         JOOOOOrJ   r`   c               #      K   t                      } 	 | V  |                                  d S # |                                  w xY wN)r   closedbs    rK   get_dbrk   r   s@      	B









s	   , Arj   rD   emailrH   returnc                     t          j        d          }t          j        d          }t          ||||          }|D ]}|                     t                                        |                                          }|s#t	          t          j        d          |          }|j        	                    |           | 
                    |           |                                  |                     |           |S )N       )rB   rC   rD   rE   rN      rB   rN   )secretstoken_urlsafer2   queryr:   	filter_byfirst	token_hexrH   appendaddcommitrefresh)	rj   rD   rl   rH   r-   r[   cs	scope_objs	            rK   create_clientr   }   s    %b))I)"--M)MsEJJJA # #HHUOO--1-55;;==	 	?!21!5!5A>>>I		""""FF1IIIIIKKKJJqMMMHrJ   c           	      V   t          j                    }t          | t          |                                          t          |t          t                    z                                             d                    |          d}t          j	        |t          t                    }|S )N)minutes )isssubiatexpscope)	algorithm)r   rF   JWT_ISSre   	timestampr   JWT_EXP_MINUTESjoinr   encodePRIVATE_KEYJWT_ALG)r-   rH   rF   payloadtokens        rK   	issue_jwtr      s    
,..C3==??##C)O<<<<GGIIJJ&!! G Jww???ELrJ   z	/register)response_modelstatus_codebodyc                 z    t          || j        | j        | j                  }t	          |j        |j                  S )N)r-   r[   )r   rU   rV   rH   rZ   rB   rC   )r   rj   clients      rK   registerr      s3    2t}d.@$+NNF&-HHHHrJ   z/token)r   credsc                    |                     t                                        |j                                                  }|r|j        |j        k    rt          dd          t          | j	                  
                    d |j	        D                       st          dd          t          |j        | j	                  }t          |t          dz  	          S )
N)rB   i  invalid_credentialsr   detailc                     h | ]	}|j         
S rI   rq   .0r   s     rK   	<setcomp>ztoken.<locals>.<setcomp>   s    %D%D%Daf%D%D%DrJ   i  scope_denied<   )ra   rd   )rv   r2   rw   usernamerx   rC   r%   r
   setrH   issubsetr   rB   r`   r   )r   r   rj   r   	jwt_tokens        rK   r   r      s     XXf''5>'::@@BBF KV]en444IJJJJt{$$%D%Dfm%D%D%DEE DNCCCC&)T[11I7KLLLLrJ   z/scopesc                 n    d |                      t                                                    D             S )Nc                     g | ]	}|j         
S rI   rq   r   s     rK   
<listcomp>zlist_scopes.<locals>.<listcomp>   s    222qAF222rJ   )rv   r:   allri   s    rK   list_scopesr      s,    22BHHUOO//112222rJ   	fhir_basec                    |                      t                                        t          j        |k    t          j        |k                                  t          j                                                                                  }|r|j	        sd S |j
        pt          }|j	        }t          j                    }|t          |t          z
            z   |k    r|j        S d S )N)seconds)rv   r   filterr-   r   order_byrB   descrx   token_timestamprd   DEFAULT_LIFETIMEr   rF   r   SAFETY_MARGIN	new_token)rj   r-   r   	token_rowrd   	issued_atrF   s          rK   _get_stored_tokenr      s     		9,9,

 

 
),##%%	&	&	   I5 t%9)9J)I
,..C 9Z-%?@@@@3FF""4rJ   	token_urlorg_idra   rd   refresh_tokenc           
          t          ||||||t          j                    |pt                    }|                     |           |                                  d S )N)r-   r   r   r   r   r   r   rd   )r   r   rF   r   r{   r|   )	rj   r-   r   r   r   ra   rd   r   r   s	            rK   _store_tokenr      sa     # 1!1	 	 	I FF9IIKKKKKrJ   c                 @   |                      t                                        t          j        |k    t          j        |k              }|#|                    t          j        |k              }|                                }|st          d|d|d|          |S )z
    Load Epic backend-services config for this client from DB.
    Adjust filters if your EpicAppConfig model uses different keys.
    Nz&EpicAppConfig not found for client_id=z fhir_base=z org_id=)rv   r   r   r-   r   r   rx   RuntimeError)rj   r-   r   r   rv   cfgs         rK   _get_epic_config_from_dbr      s     HH]##**9,9, E
 ]1V;<<
++--C 
lyy)))VV-
 
 	

 JrJ   r   c                    t          t          j                              }| j        | j        | j        t	          t          j                              |||dz   d}dt          i}t          j	        |t          d|          }|S )Nr"   )r   r   audjtir   nbfr   kidRS384)r   headers)re   timer-   r   r\   uuiduuid4KIDr   r   KEY)r   rF   r   r   r   s        rK   _build_epic_client_assertionr     s     dikk

C}}}4:<<  Sy G clGJ	  E LrJ   z/get_or_create_epic_tokenrB   c                    t                      5 }|                    t                                        t          j        | k                                              }|st          dd|            |j        }|j        }|j	        }|j
        }t          |||          }|r|cd d d            S t          |          }dd|d}	t          j        ||	ddi	          }
|
                                }|d
         }|                    d          }|                    d          }t#          ||||||||           |cd d d            S # 1 swxY w Y   d S )Ni  zNo EpicAppConfig found for id=r   client_credentialsz6urn:ietf:params:oauth:client-assertion-type:jwt-bearer)
grant_typeclient_assertion_typeclient_assertionzContent-Typez!application/x-www-form-urlencoded)datar   ra   rd   r   )rj   r-   r   r   r   ra   rd   r   )r   rv   r   r   rB   rx   r
   r-   r   r   r   r   r   requestspostjsongetr   )rB   rj   r   r-   r   token_endpointr   existingr   r   respr   ra   rd   r   s                  rK   get_or_create_epic_tokenr   (  s   	 52HH]##vm&",--uww 	
  	<<<   
 M	M	 %RI>> 	)5 5 5 5 5 5 5 5. 8<< /%] 0
 
 }#%HI
 
 
 		^,YY|,,
		/22$%!'		
 		
 		
 		
 k5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5s   BE	0BE		EE__main__)zehr.epic.readzehr.cerner.readzehr.meditech.readzpump.icu_med.readzpump.bbraun.readzpump.ivenix.readrq   rr   rs   zP[INIT] Default scopes seeded. Launch with `uvicorn connectors.token_service:app`rg   )`r   r   r   rt   r   r   typingr   r   r   fastapir   r	   r
   r   fastapi.middleware.corsr   fastapi.securityr   r   joser   pydanticr   r   r   
sqlalchemyr   r   r   r   r   sqlalchemy.ormr   r   cryptography.hazmat.primitivesr   rj   r   r   r   
epic_tokenr   r   r   r   r   PRIVATE_KEY_PATHr   r   r   openfpreadr   load_pem_private_keyr   securityapporiginsadd_middlewaremetadatar,   r2   r:   
create_allrQ   rZ   r^   r`   rk   r\   r   r   r   HTTP_201_CREATEDr   r   r   r   r   re   r   r   r   r   r>   default_scopesr   rv   rw   rx   r{   ry   r|   printrI   rJ   rK   <module>r      sm   2 2 2 2 2 2 2 2 2 2    ! ! ! ! ! ! ! !  ; ; ; ; ; ; ; ; ; ; ; ; 2 2 2 2 2 2 < < < < < < < <       0 0 0 0 0 0 0 0 0 0 B B B B B B B B B B B B B B 0 0 0 0 0 0 0 0 8 8 8 8 8 8 ) ) ) ) ) ) ) ) ) ) / / / / / / / /
 
( & 	T
C   B''))K               
T
&& G"
,-
,RWWYY
F
F
FCG G G G G G G G G G G G G G G 9;;g8999 
   %%     uDM
F;

<00dCCC
F:zz+..DAAA U U U U UT U U UV V V V VD V V V   f  % % %3 3 3 3 3 3 3 3    )   
    i       y     g C  T#Y 6    
 
d3i 
C 
 
 
 
  +kv?VWW-4WV__ I I: I7 I I I XWI
 (8,, #*'("3"3'&//M M
MM 	M M M -,M  49--%gfoo 3 3G 3 3 3 .-3  c]	   L !%   	
   d
 :   6  SM	
    8	   0 	$%%6 6 6 6 6 &%6x z	 2
 
 
   	? 	?A88E??,,!,44::<< ?uu 1 1! 4 41===>>>
		               
E
\]]]]] s7   B66B:=B:%C>>DD3BOO
O
