U
    >i                  	   @   s  d 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mZmZ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 ddlmZ eddZeG dd dZeG dd dZeee f dddZ!e"edddZ#ee" ee dddZ$ddde"edddZ%ddd d d!e"ee&e&ee d"d#d$Z'ddd d d d%ee"e
e"e"f e&e&e&ee d&d'd(Z(edd)d*Z)ddde*e"d+d,d-Z+dS ).u  
Rate limiting in this implementation relies on a cache and uses non-atomic
operations, making it vulnerable to race conditions. As a result, users may
occasionally bypass the intended rate limit due to concurrent access. However,
such race conditions are rare in practice. For example, if the limit is set to
10 requests per minute and a large number of parallel processes attempt to test
that limit, you may occasionally observe slight overruns—such as 11 or 12
requests slipping through. Nevertheless, exceeding the limit by a large margin
is highly unlikely due to the low probability of many processes entering the
critical non-atomic code section simultaneously.
    N)
namedtuple)	dataclass)
HTTPStatus)DictListOptionalTupleUnion)cache)ImproperlyConfigured)HttpRequestHttpResponse)render)TemplateDoesNotExist)RateLimitedRatezamount duration perc                   @   s<   e Zd ZU eed< eeef ed< eed< ddddZdS )SingleRateLimitUsage	cache_keycache_duration	timestampNreturnc                    s6   t  jg } fdd|D }t  j| j d S )Nc                    s   g | ]}| j kr|qS  )r   ).0tsselfr   C/tmp/pip-unpacked-wheel-upujnpc2/allauth/core/internal/ratelimit.py
<listcomp>(   s     
 z1SingleRateLimitUsage.rollback.<locals>.<listcomp>)r
   getr   setr   )r   historyr   r   r   rollback&   s    zSingleRateLimitUsage.rollback)	__name__
__module____qualname__str__annotations__r	   floatintr"   r   r   r   r   r       s   
r   c                   @   s(   e Zd ZU ee ed< ddddZdS )RateLimitUsageusageNr   c                 C   s   | j D ]}|  qd S )N)r+   r"   )r   r+   r   r   r   r"   0   s    
zRateLimitUsage.rollback)r#   r$   r%   r   r   r'   r"   r   r   r   r   r*   ,   s   
r*   r   c                 C   sp   t | dkrt| | d }| dd }ddddd}||krJtd| t |dkr\d}nt|}|||  S )	Nr      <   i  iQ )smhdzInvalid duration unit: %s)len
ValueErrorr(   )durationunitvalueZunit_mapr   r   r   parse_duration5   s    r8   )rater   c                 C   s`   |  d}t|dkr$|\}}d}n t|dkr<|\}}}nt| t|}t|}t|||S )N/   ip   )splitr3   r4   r)   r8   r   )r9   partsamountr5   perZamount_vZ
duration_vr   r   r   
parse_rateD   s    
rB   )ratesr   c                 C   s>   g }| r:|   } | r:| d}|D ]}|t|   q"|S )N,)stripr>   appendrB   )rC   retr?   partr   r   r   parse_ratesR   s    
rI   )keyuser)actionr9   c          	      C   s   ddl m} |jdkr(d| | f}nx|jdkr`|d krP| jjsJtd| j}dt|jf}n@|jdkr|d krztdt	
|d }|f}n
t|jd	d
|f|}d|S )Nr   )get_adapterr<   rK   z2ratelimit configured per user but used anonymouslyrJ   z1ratelimit configured per key but no key specifiedutf8ZallauthZrl:)Zallauth.account.adapterrM   rA   Zget_client_iprK   is_authenticatedr   r&   pkhashlibsha256encode	hexdigestr4   join)	requestrL   r9   rJ   rK   rM   sourceZkey_hashkeysr   r   r   get_cache_key]   s*    



rZ   F)rJ   rK   dry_runraise_exception)rL   r9   r[   r\   r   c                C   s   t | ||||d}t|g }t }	|rF|d |	|j krF|  q&t||jk }
|
rjt||	|jd}nd }|
r|s|	d|	 t
|||j |
s|rt|S )NrL   r9   rJ   rK   r,   )r   r   r   r   )rZ   r
   r   timer5   popr3   r@   r   insertr    r   )rW   rL   r9   rJ   rK   r[   r\   r   r!   nowallowedr+   r   r   r   _consume_single_ratex   s&    

  rc   )rJ   rK   r[   	limit_getr\   )rW   rL   configr[   rd   r\   r   c             
   C   sz   t g d}|s| jdkr|S t||}	|	s2|S d}
|	D ]2}t| ||||||d}|s`d}
 qn|j| q:|
rv|S d S )N)r+   GETT)rL   r9   rJ   rK   r[   r\   F)r*   methodrI   r   rc   r+   rF   )rW   rL   re   rJ   rK   r[   rd   r\   r+   rC   rb   r9   Zsingle_usager   r   r   consume   s,    
	rh   c                 C   sR   ddl m} zt| d|j tjdW S  tk
rL   d}t|dtjd Y S X d S )Nr   )app_settingsz429.)statusz<html>
    <head><title>Too Many Requests</title></head>
    <body>
        <h1>429 Too Many Requests</h1>
        <p>You have sent too many requests. Please try again later.</p>
    </body>
</html>z	text/html)contentcontent_typerj   )Zallauth.accountri   r   ZTEMPLATE_EXTENSIONr   TOO_MANY_REQUESTSr   r   )rW   ri   rk   r   r   r   
handler429   s    rn   )re   rL   c                C   s8   t ||}|D ] }t| ||||d}t| qd S )Nr]   )rI   r   rZ   r
   delete)rW   re   rL   rJ   rK   rC   r9   r   r   r   r   clear   s    rp   ),__doc__rR   r^   collectionsr   Zdataclassesr   httpr   typingr   r   r   r   r	   Zdjango.core.cacher
   Zdjango.core.exceptionsr   Zdjango.httpr   r   Zdjango.shortcutsr   Zdjango.template.exceptionsr   Zallauth.core.exceptionsr   r   r   r*   r)   r(   r8   r&   rB   rI   rZ   boolrc   rh   rn   dictrp   r   r   r   r   <module>   s`   
 #
#