a
    !fK                     @   s  d Z ddlmZ ddlZddlZddlZddlZ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ZW n ey   edY n0 ddlm  mZ ddlmZ ddlmZ ddlmZ ddlmZ dZdZdZdZdd ZG dd de Z!dS )a  Utilities for the Flask web framework

Provides a Flask extension that makes using OAuth2 web server flow easier.
The extension includes views that handle the entire auth flow and a
``@required`` decorator to automatically ensure that user credentials are
available.


Configuration
=============

To configure, you'll need a set of OAuth2 web application credentials from the
`Google Developer's Console <https://console.developers.google.com/project/_/apiui/credential>`__.

.. code-block:: python

    from oauth2client.contrib.flask_util import UserOAuth2

    app = Flask(__name__)

    app.config['SECRET_KEY'] = 'your-secret-key'

    app.config['GOOGLE_OAUTH2_CLIENT_SECRETS_FILE'] = 'client_secrets.json'

    # or, specify the client id and secret separately
    app.config['GOOGLE_OAUTH2_CLIENT_ID'] = 'your-client-id'
    app.config['GOOGLE_OAUTH2_CLIENT_SECRET'] = 'your-client-secret'

    oauth2 = UserOAuth2(app)


Usage
=====

Once configured, you can use the :meth:`UserOAuth2.required` decorator to
ensure that credentials are available within a view.

.. code-block:: python
   :emphasize-lines: 3,7,10

    # Note that app.route should be the outermost decorator.
    @app.route('/needs_credentials')
    @oauth2.required
    def example():
        # http is authorized with the user's credentials and can be used
        # to make http calls.
        http = oauth2.http()

        # Or, you can access the credentials directly
        credentials = oauth2.credentials

If you want credentials to be optional for a view, you can leave the decorator
off and use :meth:`UserOAuth2.has_credentials` to check.

.. code-block:: python
   :emphasize-lines: 3

    @app.route('/optional')
    def optional():
        if oauth2.has_credentials():
            return 'Credentials found!'
        else:
            return 'No credentials!'


When credentials are available, you can use :attr:`UserOAuth2.email` and
:attr:`UserOAuth2.user_id` to access information from the `ID Token
<https://developers.google.com/identity/protocols/OpenIDConnect?hl=en>`__, if
available.

.. code-block:: python
   :emphasize-lines: 4

    @app.route('/info')
    @oauth2.required
    def info():
        return "Hello, {} ({})".format(oauth2.email, oauth2.user_id)


URLs & Trigging Authorization
=============================

The extension will add two new routes to your application:

    * ``"oauth2.authorize"`` -> ``/oauth2authorize``
    * ``"oauth2.callback"`` -> ``/oauth2callback``

When configuring your OAuth2 credentials on the Google Developer's Console, be
sure to add ``http[s]://[your-app-url]/oauth2callback`` as an authorized
callback url.

Typically you don't not need to use these routes directly, just be sure to
decorate any views that require credentials with ``@oauth2.required``. If
needed, you can trigger authorization at any time by redirecting the user
to the URL returned by :meth:`UserOAuth2.authorize_url`.

.. code-block:: python
   :emphasize-lines: 3

    @app.route('/login')
    def login():
        return oauth2.authorize_url("/")


Incremental Auth
================

This extension also supports `Incremental Auth <https://developers.google.com/identity/protocols/OAuth2WebServer?hl=en#incrementalAuth>`__. To enable it,
configure the extension with ``include_granted_scopes``.

.. code-block:: python

    oauth2 = UserOAuth2(app, include_granted_scopes=True)

Then specify any additional scopes needed on the decorator, for example:

.. code-block:: python
   :emphasize-lines: 2,7

    @app.route('/drive')
    @oauth2.required(scopes=["https://www.googleapis.com/auth/drive"])
    def requires_drive():
        ...

    @app.route('/calendar')
    @oauth2.required(scopes=["https://www.googleapis.com/auth/calendar"])
    def requires_calendar():
        ...

The decorator will ensure that the the user has authorized all specified scopes
before allowing them to access the view, and will also ensure that credentials
do not lose any previously authorized scopes.


Storage
=======

By default, the extension uses a Flask session-based storage solution. This
means that credentials are only available for the duration of a session. It
also means that with Flask's default configuration, the credentials will be
visible in the session cookie. It's highly recommended to use database-backed
session and to use https whenever handling user credentials.

If you need the credentials to be available longer than a user session or
available outside of a request context, you will need to implement your own
:class:`oauth2client.Storage`.
    wrapsN)	Blueprint)_app_ctx_stack)current_app)redirect)request)sessionurl_forz/The flask utilities require flask 0.9 or newer.)client)clientsecrets)	transport)dictionary_storage)emailgoogle_oauth2_credentialszgoogle_oauth2_flow_{0}Zgoogle_oauth2_csrf_tokenc                 C   s,   t t| d}|du rdS t|S dS )zZRetrieves the flow instance associated with a given CSRF token from
    the Flask session.N)r	   pop	_FLOW_KEYformatpickleloads)
csrf_tokenZflow_pickle r   `/var/www/html/python-backend/venv/lib/python3.9/site-packages/oauth2client/contrib/flask_util.py_get_flow_for_token   s    
r   c                   @   s   e Zd ZdZd!ddZd"ddZdd Zd	d
 Zd#ddZdd Z	dd Z
dd Zedd Zdd Zedd Zedd Zdd Zd$ddZdd  ZdS )%
UserOAuth2a  Flask extension for making OAuth 2.0 easier.

    Configuration values:

        * ``GOOGLE_OAUTH2_CLIENT_SECRETS_FILE`` path to a client secrets json
          file, obtained from the credentials screen in the Google Developers
          console.
        * ``GOOGLE_OAUTH2_CLIENT_ID`` the oauth2 credentials' client ID. This
          is only needed if ``GOOGLE_OAUTH2_CLIENT_SECRETS_FILE`` is not
          specified.
        * ``GOOGLE_OAUTH2_CLIENT_SECRET`` the oauth2 credentials' client
          secret. This is only needed if ``GOOGLE_OAUTH2_CLIENT_SECRETS_FILE``
          is not specified.

    If app is specified, all arguments will be passed along to init_app.

    If no app is specified, then you should call init_app in your application
    factory to finish initialization.
    Nc                 O   s*   || _ |d ur&| j|g|R i | d S )N)appinit_app)selfr   argskwargsr   r   r   __init__   s    zUserOAuth2.__init__c           	      K   sj   || _ || _|| _|du r(tjttd}|| _|du rD|j	dt
}|| _| ||| ||   dS )aH  Initialize this extension for the given app.

        Arguments:
            app: A Flask application.
            scopes: Optional list of scopes to authorize.
            client_secrets_file: Path to a file containing client secrets. You
                can also specify the GOOGLE_OAUTH2_CLIENT_SECRETS_FILE config
                value.
            client_id: If not specifying a client secrets file, specify the
                OAuth2 client id. You can also specify the
                GOOGLE_OAUTH2_CLIENT_ID config value. You must also provide a
                client secret.
            client_secret: The OAuth2 client secret. You can also specify the
                GOOGLE_OAUTH2_CLIENT_SECRET config value.
            authorize_callback: A function that is executed after successful
                user authorization.
            storage: A oauth2client.client.Storage subclass for storing the
                credentials. By default, this is a Flask session based storage.
            kwargs: Any additional args are passed along to the Flow
                constructor.
        N)keyZGOOGLE_OAUTH2_SCOPES)r   authorize_callbackflow_kwargsr   ZDictionaryStorager	   _CREDENTIALS_KEYstorageconfigget_DEFAULT_SCOPESscopes_load_configZregister_blueprint_create_blueprint)	r   r   r*   client_secrets_file	client_idclient_secretr#   r&   r    r   r   r   r      s    zUserOAuth2.init_appc                 C   s   |r|r|| | _ | _dS |r,| | dS d| jjv rN| | jjd  dS z"| jjd | jjd  | _ | _W n ty   tdY n0 dS )a  Loads oauth2 configuration in order of priority.

        Priority:
            1. Config passed to the constructor or init_app.
            2. Config passed via the GOOGLE_OAUTH2_CLIENT_SECRETS_FILE app
               config.
            3. Config passed via the GOOGLE_OAUTH2_CLIENT_ID and
               GOOGLE_OAUTH2_CLIENT_SECRET app config.

        Raises:
            ValueError if no config could be found.
        NZ!GOOGLE_OAUTH2_CLIENT_SECRETS_FILEZGOOGLE_OAUTH2_CLIENT_IDZGOOGLE_OAUTH2_CLIENT_SECRETzOAuth2 configuration could not be found. Either specify the client_secrets_file or client_id and client_secret or set the app configuration variables GOOGLE_OAUTH2_CLIENT_SECRETS_FILE or GOOGLE_OAUTH2_CLIENT_ID and GOOGLE_OAUTH2_CLIENT_SECRET.)r.   r/   _load_client_secretsr   r'   KeyError
ValueError)r   r-   r.   r/   r   r   r   r+     s&    



zUserOAuth2._load_configc                 C   s>   t |\}}|t jkr&td||d | _|d | _dS )z-Loads client secrets from the given filename.z+The flow specified in {0} is not supported.r.   r/   N)r   ZloadfileZTYPE_WEBr2   r   r.   r/   )r   filenameZclient_typeZclient_infor   r   r   r0   :  s    

zUserOAuth2._load_client_secretsc           
   
   K   s   t td }|tt< t||d}| j	
 }|| |dg }t| jt|}tjf | j| j||tdddd|}t|}	t|t|	< |S )zCreates a Web Server Flowi   )r   
return_urlr*   zoauth2.callbackT)Z	_external)r.   r/   scopestateZredirect_uri)hashlibsha256osurandom	hexdigestr	   	_CSRF_KEYjsondumpsr$   copyupdater   setr*   unionr   ZOAuth2WebServerFlowr.   r/   r   r   r   r   )
r   r4   r    r   r6   kwZextra_scopesr*   flowZflow_keyr   r   r   
_make_flowE  s,    



zUserOAuth2._make_flowc                 C   s.   t dt}|dd| j |dd| j |S )NZoauth2z/oauth2authorize	authorizez/oauth2callbackcallback)r   __name__Zadd_url_ruleauthorize_viewcallback_view)r   bpr   r   r   r,   d  s    
zUserOAuth2._create_blueprintc                 C   s\   t j }t jd|d< |dd}|du r8t jp6d}| jf d|i|}| }t|S )z|Flask view that starts the authorization flow.

        Starts flow by redirecting the user to the OAuth2 provider.
        r*   r4   N/)	r   r   to_dictgetlistr   ZreferrerrE   Zstep1_get_authorize_urlr   )r   r   r4   rD   auth_urlr   r   r   rI   k  s    

zUserOAuth2.authorize_viewc              
   C   sd  dt jv r<t jdt jdd}t|}d|tjfS z t jd }tt	 }t jd }W n t
yx   dtjf Y S 0 zt|}|d }|d	 }W n  tt
fy   d
tjf Y S 0 ||krd
tjfS t|}|du rd
tjfS z||}	W nH tjy< }
 z,tj|
 d|
}|tjfW  Y d}
~
S d}
~
0 0 | j|	 | jr\| |	 t|S )zFlask view that handles the user's return from OAuth2 provider.

        On return, exchanges the authorization code for credentials and stores
        the credentials.
        errorZerror_description zAuthorization failed: {0}r6   codezInvalid requestr   r4   zInvalid request stateNzAn error occurred: {0})r   r   r(   
markupsafeescaper   httplibBAD_REQUESTr	   r<   r1   r=   r   r2   r   Zstep2_exchanger   ZFlowExchangeErrorr   logger	exceptionr&   putr#   r   )r   reasonZencoded_stateZserver_csrfrR   r6   Zclient_csrfr4   rD   credentialsZexchange_errorcontentr   r   r   rJ     sF    






 
zUserOAuth2.callback_viewc                 C   s"   t j}t|ts| j |_|jS )z<The credentials for the current user or None if unavailable.)r   tophasattrr%   r&   r(   r   )r   ctxr   r   r   r[     s    
zUserOAuth2.credentialsc                 C   s&   | j s
dS | j jr| j jsdS dS dS )zAReturns True if there are valid credentials for the current user.FTN)r[   Zaccess_token_expiredZrefresh_tokenr   r   r   r   has_credentials  s    zUserOAuth2.has_credentialsc                 C   sF   | j s
dS z| j jd W S  ty@   tjd| j j Y n0 dS )a*  Returns the user's email address or None if there are no credentials.

        The email address is provided by the current credentials' id_token.
        This should not be used as unique identifier as the user can change
        their email. If you need a unique identifier, use user_id.
        Nr   Invalid id_token {0}r[   Zid_tokenr1   r   rW   rP   r   r`   r   r   r   r     s    zUserOAuth2.emailc                 C   sF   | j s
dS z| j jd W S  ty@   tjd| j j Y n0 dS )zReturns the a unique identifier for the user

        Returns None if there are no credentials.

        The id is provided by the current credentials' id_token.
        Nsubrb   rc   r`   r   r   r   user_id  s    zUserOAuth2.user_idc                 K   s   t dd|i|S )a,  Creates a URL that can be used to start the authorization flow.

        When the user is directed to the URL, the authorization flow will
        begin. Once complete, the user will be redirected to the specified
        return URL.

        Any kwargs are passed into the flow constructor.
        oauth2.authorizer4   )rf   r
   )r   r4   r    r   r   r   authorize_url  s    	zUserOAuth2.authorize_urlc                    s$    fdd}|r||S |S dS )a
  Decorator to require OAuth2 credentials for a view.

        If credentials are not available for the current user, then they will
        be redirected to the authorization flow. Once complete, the user will
        be redirected back to the original page.
        c                    s   t   fdd}|S )Nc                     s     dtj}tj}d ur,|tO } r@|jjO }t|} rjj|rj| i |S j	|fd|i }t
|S d S )Nr4   r*   )r   r   urlrA   r*   ra   r[   listZ
has_scopesrg   r   )r   r    r4   Zrequested_scopesrO   )decorator_kwargsr*   r   wrapped_functionr   r   required_wrapper  s&    

zDUserOAuth2.required.<locals>.curry_wrapper.<locals>.required_wrapperr   )rk   rl   rj   r*   r   )rk   r   curry_wrapper  s    z*UserOAuth2.required.<locals>.curry_wrapperNr   )r   Zdecorated_functionr*   rj   rn   r   rm   r   required  s    	zUserOAuth2.requiredc                 O   s&   | j std| j tj|i |S )a  Returns an authorized http instance.

        Can only be called if there are valid credentials for the user, such
        as inside of a view that is decorated with @required.

        Args:
            *args: Positional arguments passed to httplib2.Http constructor.
            **kwargs: Positional arguments passed to httplib2.Http constructor.

        Raises:
            ValueError if no credentials are available.
        zNo credentials available.)r[   r2   rF   r   Zget_http_object)r   r   r    r   r   r   http  s
    zUserOAuth2.http)N)NNNNNN)N)NN)rH   
__module____qualname____doc__r!   r   r+   r0   rE   r,   rI   rJ   propertyr[   ra   r   re   rg   ro   rp   r   r   r   r   r      s,   
   
)&
3
	


+r   )"rs   	functoolsr   r7   r=   r9   r   Zflaskr   r   r   r   r   r	   r   rS   ImportErrorZsix.moves.http_clientmoveshttp_clientrU   Zoauth2clientr   r   r   Zoauth2client.contribr   r)   r%   r   r<   r   objectr   r   r   r   r   <module>   s8    