
    g@                     V   d dl m Z mZmZ d dlmZmZ d dlZd dlmZ d dl	Z	d dl
Z
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mZ 	 d d
lmZ  ed      ej4                   ed      <   dZdZddZddZd Zd Z d Z!d Z"d Z#d Z$d Z%d Z&d Z' G d de(      Z)y# e$ r	 d d
lmZ Y [w xY w)    )datetime	timedeltatimezone)quoteunquoteN)
check_hash)logger)MonitorDatabase)	timestamp)Usersrefresh_users)PlexTV)MorselSameSitesamesiteHS256tautulli_token_c                 <   d }d }| r't        | |      }|j                         }|r	| }|d   }ny |r|rt               }|j                  |      }|t	        |d         k7  ry t
        j                  j                  r	|d   r|dfS |d   r|d   ry t
        j                  j                  sy t        ||      }|j                         }|rit               }		 t        j                  d|d	   z         |	j                  d
||d   g      }
|
rt                |dfS t        j                  d|d	   z         y t        j                  d|d	   z         y | rt        j                  d       y y # t         $ r)}t        j                  d|d	   d|d       Y d }~y d }~ww xY w)Ntokenheadersuser_id)r   is_adminadminallow_guestdeleted_userzDTautulli WebAuth :: Registering token for user '%s' in the database.usernamez3UPDATE users SET server_token = ? WHERE user_id = ?guestz=Tautulli WebAuth :: Unable to register user '%s' in database.z-Tautulli WebAuth :: Unable to register user 'z' in database: .zJTautulli WebAuth :: Unable to retrieve Plex.tv server token for user '%s'.zITautulli WebAuth :: Unable to retrieve Plex.tv user token for Plex OAuth.)r   get_plex_account_detailsr   get_detailsstrplexpyCONFIGHTTP_PLEX_ADMINALLOW_GUEST_ACCESSget_server_tokenr
   r	   debugactionr   warn	Exception)r   r   
user_tokenr   plex_tv	plex_user	user_datauser_detailsserver_token
monitor_dbresultes               /opt/Tautulli/plexpy/webauth.pyplex_user_loginr6   1   s   JG ug6446	J	*Gg G	 ,,W,=c,y122]]**|J/G((m,^0L }}// z7;//1 )*Jc+J78 9#**+`,8,y:Q+RT !O'00KK _".z":!; < KKd&z23 4	_` 
  +J7< =s   AE) E) )	F2FFc                 |   | rg|ret         j                  j                  rKd| d}| t         j                  j                  k(  r)t	        |t         j                  j                        rd|dfS t         j                  j
                  s|dk(  s4t         j                  j                  rt        ||      }|d|d   |d   fS y	)
zyVerifies credentials for username and password.
    Returns True and the user group on success or False and no user groupN)r   r   Tr   1r   r      )FNN)r#   r$   HTTP_PASSWORDHTTP_USERNAMEr   r%   r&   r6   )r   passwordr   admin_loginr   r0   
plex_logins          r5   check_credentialsr?   w   s     H==&&'+BL6==666:hPVP]P]PkPk;l\722}}$$[C-?FMMDdDd$5'B
!A
155    c                      t        t        t        j                  j                  z         } t
        j                  j                  j                  |       }|r|j                  S y N)
r"   JWT_COOKIE_NAMEr#   r$   PMS_UUIDcherrypyrequestcookiegetvalue)
jwt_cookie	jwt_tokens     r5   get_jwt_tokenrL      sI    _v}}'='==>J  ''++J7I r@   c                  *   t               } | r^	 t        j                  | t        j                  j
                  t        d      t        g      }t               j                  |       sy |S y # t        j                  t        j                  f$ r Y y w xY w)N
   )seconds)leeway
algorithmsrK   )rL   jwtdecoder#   r$   
JWT_SECRETr   JWT_ALGORITHMDecodeErrorExpiredSignatureErrorr   get_user_login)rK   payloads     r5   check_jwt_tokenr[      s    I	jj6==33Ib<Q_l^mG w%%	%: 
 !:!:; 		s   A A, ,#BBc                     t         j                  j                  j                  dd      }|t	               }|rG|t         j                  _        |D ],  } |       rt        j                  t        j                         yt         j                  j                  d   }|rdt        |      z   }t        j                  t        j                  dz   |z         y)zA tool that looks in config for 'auth.require'. If found and it
    is not None, a login is required and the entry is evaluated as a list of
    conditions that the user must fulfillauth.requireNREQUEST_URI?redirect_uri=zauth/logout)rE   rF   configrH   r[   loginHTTPRedirectr#   	HTTP_ROOTwsgi_environr   )argskwargs
conditionsrZ   	conditionredirect_uris         r5   
check_authrj      s     !!((,,^TBJ!#%,H"' B	 {"//0@0@AAB $++88GL/%2EE''(8(8=(H<(WXX! r@   c                        fd}|S )zLA decorator that appends conditions to the auth.require config
    variable.c                     t        | d      st               | _        d| j                  vrg | j                  d<   | j                  d   j                         | S )N
_cp_configr]   )hasattrdictrm   extend)frg   s    r5   decoratezrequireAuth.<locals>.decorate   sL    q,'6AL-+-ALL(	^$++J7r@    )rg   rr   s   ` r5   requireAuthrt      s     Or@   c                       fdS )Nc                  |    t         j                  j                  xr  t         j                  j                  d    k(  S )N
user_grouprE   rF   ra   rw   s   r5   <lambda>zmember_of.<locals>.<lambda>   s/    8##))`h.>.>.D.D\.RV`.` r@   rs   ry   s   `r5   	member_ofr{      s	    ``r@   c                       fdS )Nc                  |    t         j                  j                  xr  t         j                  j                  d    k(  S )Nuserrx   	user_names   r5   rz   zname_is.<locals>.<lambda>   s/    8##))Yh.>.>.D.DV.LPY.Y r@   rs   r   s   `r5   name_isr      s	    YYr@   c                        fd}|S )z+Returns True if any of the conditions matchc                  &    D ]  }  |        s y y)NTFrs   crg   s    r5   checkzany_of.<locals>.check   s     	As	 r@   rs   rg   r   s   ` r5   any_ofr      s    
 Lr@   c                        fd}|S )z+Returns True if all of the conditions matchc                  &    D ]  }  |        r y y)NFTrs   r   s    r5   r   zall_of.<locals>.check   s     	A3	 r@   rs   r   s   ` r5   all_ofr      s    
 Lr@   c                     t               }|j                  d| | g      }	 |d   d   }	 t        d |D              }t        ||t
        j                  j                  z
        }|D cg c]  }|d   |k\  s|d   r| }}t        |      t
        j                  j                  k\  r2t        |t               t
        j                  j                  z
  z
  d      S y # t        $ r d}Y w xY w# t        $ r d}Y w xY wc c}w )NzSELECT timestamp, success FROM user_login WHERE ip_address = ? AND timestamp >= ( SELECT CASE WHEN MAX(timestamp) IS NULL THEN 0 ELSE MAX(timestamp) END FROM user_login WHERE ip_address = ? AND success = 1) ORDER BY timestamp DESCr   r   c              3   2   K   | ]  }|d    s	|d     yw)successr   Nrs   ).0ra   s     r5   	<genexpr>z#check_rate_limit.<locals>.<genexpr>   s     V%U9EU5-Vs   

r   )r
   select
IndexErrormax
ValueErrorr#   r$   !HTTP_RATE_LIMIT_ATTEMPTS_INTERVALlenHTTP_RATE_LIMIT_ATTEMPTSr   HTTP_RATE_LIMIT_LOCKOUT_TIME)
ip_addressr2   r3   last_timestamplast_successmax_timestampra   attemptss           r5   check_rate_limitr      s    "J  9 !+J79F;/V6VV nv}}7f7f&fgM#)j%U;-?=-PY^_hYijHj
8}>>>>Y[6==3]3]%]^`abb ?  
   ks4   C C* &C;4C;:C;C'&C'*C87C8c                   $   e Zd Zd Z	 	 ddZddZddZej                  d        Z	ej                  dd       Z
ej                  dd       Zej                  ej                  j                         dd	              Zej                  dd
       Zy)AuthControllerc                     t         j                  j                  st         j                  j                  ry t	        j
                  t         j                        rB   )r#   r$   HTTP_BASIC_AUTHr:   rE   rb   rc   )selfs    r5   check_auth_enabledz!AuthController.check_auth_enabled  s5    }},,1L1L##F$4$455r@   Nc                    t         j                  j                  j                  }t         j                  j                  }	t         j                  j
                  j                  d      }
t               j                  |||||	|
|||	       |r4|rdnd}t        j                  d|j                         d|d|d       y	y	)
zCalled on successful loginz
User-Agent)	r   r~   rw   r   host
user_agentr   expiryrK   
Plex OAuthformTautulli WebAuth ::  user 'z' logged into Tautulli using z login.N)rE   rF   remoteipbaser   rH   r   set_user_loginr	   r(   
capitalize)r   r   r   rw   r   oauthr   rK   r   r   r   	use_oauths               r5   on_loginzAuthController.on_login  s    
 %%,,//
$$%%--11,?
w$,*4*4$(*4'.&,)2 	 	4 (-6ILL&113XyJ K r@   c                     t               }|rt               j                  |       t        j                  d|j                         d|d       y)zCalled on logoutrR   r   r   z' logged out of Tautulli.N)rL   r   clear_user_login_tokenr	   r(   r   )r   r   rw   rK   s       r5   	on_logoutzAuthController.on_logout*  s;    !O	G**Y*?S]ShShSjltuvr@   c                 6    ddl m}  |ddt        |            S )Nr   )serve_templatez
login.htmlLogin)template_nametitleri   )plexpy.webserver   r   )r   ri   r   s      r5   get_loginformzAuthController.get_loginform2  s    2LV]^jVkllr@   c                 N    t        j                  t        j                  dz         )N
auth/login)rE   rb   r#   rc   )r   re   rf   s      r5   indexzAuthController.index6  s    ##F$4$4|$CDDr@   c                 F    | j                          | j                  |      S )N)ri   )r   r   )r   ri   re   rf   s       r5   ra   zAuthController.login:  s"    !!!|!<<r@   c                    | j                          t               }|r| j                  |d   |d          t        t        t
        j                  j                  z         }dt        j                  j                  |<   dt        j                  j                  |   d<   t
        j                  j                  d      xs dt        j                  j                  |   d<   t
        j                  dk7  r |d	z   t        j                  j                  d
<   d t        j                  _        |rd|z   }t        j                   t
        j                  dz   |z         )Nr~   rw   )r   rw    r   max-age/pathz=""; max-age=0; path=/z
Set-Cookier_   r   )r   r[   r   r"   rC   r#   r$   rD   rE   responserG   rc   rstripr   rF   ra   rb   )r   ri   re   rf   rZ   rJ   s         r5   logoutzAuthController.logout@  s   !!#NNGFO&-l&;  = 6==+A+AAB
/1  ,:;  ,Y77=7G7G7N7Ns7S7ZWZ  ,V4s"6@C[6[H%%l3!%+l:L##F$4$4|$Cl$RSSr@   c           	      ,   t         j                  j                  dk7  rdt         j                  _        dddS t         j                  j
                  j                  }t        |      }	|	rQt        j                  d|z         ddd}
dt         j                  _        |	t         j                  j                  d	<   |
S dd
d}
t        |||||      \  }}}|r|dk(  rt        d      nt        d      }t        j                  t        j                         |z   }|d   |d   ||d}t#        j$                  |t&        j(                  j*                  t,              }| j/                  |d   |d   |dt1        |      ||       t3        t4        t&        j(                  j6                  z         }|t         j                  j8                  |<   t;        |j=                               t         j                  j8                  |   d<   t&        j>                  jA                  d      xs dt         j                  j8                  |   d<   dt         j                  j8                  |   d<   dt         j                  j8                  |   d<   |t         j                  _!        dt         j                  _        d|t&        j(                  j6                  d S |dk(  rC|rA| j/                  |!       t        j                  d"|z         d#t         j                  _        |
S |rA| j/                  |!       t        j                  d$|z         d#t         j                  _        |
S |r?| j/                  d%d&       t        j                  d'       d#t         j                  _        |
S y )(NPOSTi  errorzSign in using POST.)statusmessagez@Tautulli WebAuth :: Too many incorrect login attempts from '%s'.zToo many login attempts.i  zRetry-AfterzInvalid credentials.)r   r<   r   r=   r   r8      )days<   )minutes)tzr   r   )r   r~   rw   exp)	algorithmT)r   r   rw   r   r   r   rK   r   r   r   httponlylaxr      r   )r   r   uuid)r   z:Tautulli WebAuth :: Invalid admin login attempt from '%s'.i  z9Tautulli WebAuth :: Invalid user login attempt from '%s'.r   )r   r   z5Tautulli WebAuth :: Invalid Plex OAuth login attempt.)"rE   rF   methodr   r   r   r   r   r	   r(   r   r?   r   r   nowr   utcrS   encoder#   r$   rU   rV   r   boolr"   rC   rD   rG   inttotal_secondsrc   r   ra   )r   r   r<   r   remember_mer=   re   rf   r   
rate_limiterror_messagevalid_loginr0   rw   
time_deltar   rZ   rK   rJ   s                      r5   signinzAuthController.signinY  s    ""f,'*H$%2GHH%%,,//
%j1
LL[^hhi'.;UVM'*H$7AH%%m4  #*7MN0A8KSHMNYJP	1R-\: /:c/A+yY[G\J\\X\\2Z?F (	2$Z0(	G 

7FMM,D,DP]^IMM<
#;".y"9%/"& $U!'$-  / _v}}/E/EEFJ3<H$$Z0>A*BZBZB\>]H$$Z0;;A;K;K;R;RSV;W;^[^H$$Z08?CH$$Z0<?DH$$Z0<%,H"'*H$')V]]E[E[\\CHMM8M,LLUX``a'*H$  MM8M,LLTW__`'*H$  MM<tM<LLPQ'*H$  	 r@   c                     t         j                  j                  d      }|j                  |      r|t	        |      d  }t        j                  t         j                  |j                  d      z         )Nr   )r#   rc   r   
startswithr   rE   rb   strip)r   ri   re   rf   roots        r5   redirectzAuthController.redirect  s]    &&s+""4('D	
3L##F$4$4|7I7I#7N$NOOr@   )NNNFFNNrB   )r   )NNN0r   )__name__
__module____qualname__r   r   r   r   rE   exposer   ra   r   toolsjson_outr   r   rs   r@   r5   r   r     s    6
 [`(,K0wm __E E __= =
 __T T0 __^^G!  G!R __P Pr@   r   )NN)NNNr   N)*r   r   r   urllib.parser   r   rE   hashing_passwordsr   rS   r#   r	   plexpy.databaser
   plexpy.helpersr   plexpy.usersr   r   plexpy.plextvr   http.cookiesr   ImportErrorCookier"   	_reservedrV   rC   r6   r?   rL   r[   rj   rt   r{   r   r   r   r   objectr   rs   r@   r5   <module>r      s   . 3 2 '  ( 
   + $ -  # %(
O  Z !#CL$"Y0
(aZc:^PV ^PE  s   B B('B(