U
    gg'                     @   s  d dl Z d dl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	 d dlm
Z
 d dlmZ d dlmZ d d	lmZ d d
lmZ d dlmZ d dlmZ d dlmZ dZeeZdddZd ddZd!ddZG dd deZG dd dZG dd deZdd Z dS )"    N)urlparse)	Blueprint)current_app)g)request)session)BadData)SignatureExpired)URLSafeTimedSerializer)
BadRequest)ValidationError)CSRF)generate_csrfvalidate_csrfCSRFProtectc                 C   s   t | dtjdd} t |dddd}|tkrt| dd}|tkrVtt	d	
 t|< z|t| }W n: tk
r   tt	d	
 t|< |t| }Y nX tt|| t|S )
a  Generate a CSRF token. The token is cached for a request, so multiple
    calls to this function will generate the same token.

    During testing, it might be useful to access the signed token in
    ``g.csrf_token`` and the raw token in ``session['csrf_token']``.

    :param secret_key: Used to securely sign the token. Default is
        ``WTF_CSRF_SECRET_KEY`` or ``SECRET_KEY``.
    :param token_key: Key where token is stored in session for comparison.
        Default is ``WTF_CSRF_FIELD_NAME`` or ``'csrf_token'``.
    WTF_CSRF_SECRET_KEY%A secret key is required to use CSRF.messageWTF_CSRF_FIELD_NAME
csrf_token%A field name is required to use CSRF.wtf-csrf-tokensalt@   )_get_configr   
secret_keyr   r
   r   hashlibsha1osurandom	hexdigestdumps	TypeErrorsetattrget)r   	token_key
field_namestoken r+   2/tmp/pip-unpacked-wheel-htolim9p/flask_wtf/csrf.pyr      s.    r   c              
   C   s   t |dtjdd}t |dddd}t |ddd	d
}| s>td|tkrNtdt|dd}z|j| |d}W nV tk
r } ztd|W 5 d}~X Y n, tk
r } ztd|W 5 d}~X Y nX t	
t| |stddS )a  Check if the given data is a valid CSRF token. This compares the given
    signed token to the one stored in the session.

    :param data: The signed CSRF token to be checked.
    :param secret_key: Used to securely sign the token. Default is
        ``WTF_CSRF_SECRET_KEY`` or ``SECRET_KEY``.
    :param time_limit: Number of seconds that the token is valid. Default is
        ``WTF_CSRF_TIME_LIMIT`` or 3600 seconds (60 minutes).
    :param token_key: Key where token is stored in session for comparison.
        Default is ``WTF_CSRF_FIELD_NAME`` or ``'csrf_token'``.

    :raises ValidationError: Contains the reason that validation failed.

    .. versionchanged:: 0.14
        Raises ``ValidationError`` with a specific error message rather than
        returning ``True`` or ``False``.
    r   r   r   r   r   r   WTF_CSRF_TIME_LIMIT  F)requiredzThe CSRF token is missing.z"The CSRF session token is missing.r   r   )Zmax_agezThe CSRF token has expired.NzThe CSRF token is invalid.zThe CSRF tokens do not match.)r   r   r   r   r   r
   loadsr	   r   hmaccompare_digest)datar   Z
time_limitr'   r(   r)   r*   er+   r+   r,   r   B   s4    r   TCSRF is not configured.c                 C   s.   | dkrt j||} |r*| dkr*t|| S )a  Find config value based on provided value, Flask config, and default
    value.

    :param value: already provided config value
    :param config_name: Flask ``config`` key
    :param default: default value if not provided or configured
    :param required: whether the value must not be ``None``
    :param message: error message if required config is not found
    :raises KeyError: if required config is not found
    N)r   configr&   RuntimeError)valueZconfig_namedefaultr/   r   r+   r+   r,   r   v   s
    r   c                       s,   e Zd Z fddZdd Zdd Z  ZS )_FlaskFormCSRFc                    s   |j | _ t |S N)metasuper
setup_form)selfform	__class__r+   r,   r>      s    z_FlaskFormCSRF.setup_formc                 C   s   t | jj| jjdS )N)r   r'   )r   r<   csrf_secretcsrf_field_name)r?   Zcsrf_token_fieldr+   r+   r,   generate_csrf_token   s     z"_FlaskFormCSRF.generate_csrf_tokenc              
   C   sj   t ddrd S z t|j| jj| jj| jj W n4 tk
rd } zt	
|jd   W 5 d }~X Y nX d S )N
csrf_validFr   )r   r&   r   r3   r<   rC   Zcsrf_time_limitrD   r   loggerinfoargs)r?   r@   fieldr4   r+   r+   r,   validate_csrf_token   s    z"_FlaskFormCSRF.validate_csrf_token)__name__
__module____qualname__r>   rE   rK   __classcell__r+   r+   rA   r,   r:      s   r:   c                   @   sB   e Zd ZdZdddZdd Zdd Zd	d
 Zdd Zdd Z	dS )r   a[  Enable CSRF protection globally for a Flask app.

    ::

        app = Flask(__name__)
        csrf = CSRFProtect(app)

    Checks the ``csrf_token`` field sent with forms, or the ``X-CSRFToken``
    header sent with JavaScript requests. Render the token in templates using
    ``{{ csrf_token() }}``.

    See the :ref:`csrf` documentation.
    Nc                 C   s"   t  | _t  | _|r| | d S r;   )set_exempt_views_exempt_blueprintsinit_app)r?   appr+   r+   r,   __init__   s    zCSRFProtect.__init__c                    s    j d<  jdd  jdd t jddddd	g jd<  jd
d  jdddg  jdd  jdd t jjd<  dd   j	 fdd}d S )NZcsrfWTF_CSRF_ENABLEDTWTF_CSRF_CHECK_DEFAULTWTF_CSRF_METHODSPOSTPUTPATCHDELETEr   r   WTF_CSRF_HEADERSzX-CSRFTokenzX-CSRF-Tokenr-   r.   WTF_CSRF_SSL_STRICTc                   S   s   dt iS )Nr   )r   r+   r+   r+   r,   <lambda>       z&CSRFProtect.init_app.<locals>.<lambda>c                     s    j d sd S  j d sd S tj j d kr0d S tjs:d S  jtjjkrRd S  jtj} | j	 d| j
 }|jkrd S   d S )NrV   rW   rX   .)r6   r   methodZendpointZ
blueprintsr&   Z	blueprintrR   Zview_functionsrM   rL   rQ   protect)viewdestrT   r?   r+   r,   csrf_protect   s    


z*CSRFProtect.init_app.<locals>.csrf_protect)

extensionsr6   
setdefaultrP   r&   r   Z	jinja_envglobalsZcontext_processorZbefore_request)r?   rT   rg   r+   rf   r,   rS      s    

zCSRFProtect.init_appc                 C   sv   t jd }tj|}|r|S tjD ]$}||r$tj| }|r$|  S q$t jd D ]}tj|}|rT|  S qTd S )Nr   r]   )r   r6   r   r@   r&   endswithheaders)r?   r(   Z
base_tokenkeyr   header_namer+   r+   r,   _get_csrf_token   s    





zCSRFProtect._get_csrf_tokenc              
   C   s   t jtjd krd S zt|   W nB tk
rf } z$t|j	d  | 
|j	d  W 5 d }~X Y nX t jrtjd rt js| 
d dt j d}tt j|s| 
d dt_d S )	NrX   r   r^   zThe referrer header is missing.zhttps:///z%The referrer does not match the host.T)r   rb   r   r6   r   ro   r   rG   rH   rI   _error_responseZ	is_secureZreferrerhostsame_originr   rF   )r?   r4   Zgood_referrerr+   r+   r,   rc      s    "

zCSRFProtect.protectc                 C   sL   t |tr| j| |S t |tr*|}nd|j|jf}| j| |S )a  Mark a view or blueprint to be excluded from CSRF protection.

        ::

            @app.route('/some-view', methods=['POST'])
            @csrf.exempt
            def some_view():
                ...

        ::

            bp = Blueprint(...)
            csrf.exempt(bp)

        ra   )	
isinstancer   rR   addstrjoinrM   rL   rQ   )r?   rd   Zview_locationr+   r+   r,   exempt  s    

zCSRFProtect.exemptc                 C   s   t |d S r;   )	CSRFError)r?   reasonr+   r+   r,   rq   2  s    zCSRFProtect._error_response)N)
rL   rM   rN   __doc__rU   rS   ro   rc   rx   rq   r+   r+   r+   r,   r      s   
)r   c                   @   s   e Zd ZdZdZdS )ry   zRaise if the client sends invalid CSRF data with the request.

    Generates a 400 Bad Request response with the failure reason by default.
    Customize the response by registering a handler with
    :meth:`flask.Flask.errorhandler`.
    zCSRF validation failed.N)rL   rM   rN   r{   descriptionr+   r+   r+   r,   ry   6  s   ry   c                 C   s4   t | }t |}|j|jko2|j|jko2|j|jkS r;   )r   schemehostnameport)Zcurrent_uriZcompare_uricurrentcomparer+   r+   r,   rs   A  s    

rs   )NN)NNN)NTr5   )!r   r1   loggingr    urllib.parser   Zflaskr   r   r   r   r   Zitsdangerousr   r	   r
   Zwerkzeug.exceptionsr   Zwtformsr   Zwtforms.csrf.corer   __all__	getLoggerrL   rG   r   r   r   r:   r   ry   rs   r+   r+   r+   r,   <module>   s8   

+
5     
 