
    TD                         S r SSKrSSKrSSKJr  SSKJr  SSKrSSKJ	r	  SSK
Jr  SSKJr  SSKJr  S	rS
rS rS rS r " S S\5      r\" \R.                  R0                  5      rSrS rS rS r " S S\5      rg)a  Utilities for the Django web framework.

Provides Django views and helpers the make using the OAuth2 web server
flow easier. It includes an ``oauth_required`` decorator to automatically
ensure that user credentials are available, and an ``oauth_enabled`` decorator
to check if the user has authorized, and helper shortcuts to create the
authorization URL otherwise.

There are two basic use cases supported. The first is using Google OAuth as the
primary form of authentication, which is the simpler approach recommended
for applications without their own user system.

The second use case is adding Google OAuth credentials to an
existing Django model containing a Django user field. Most of the
configuration is the same, except for `GOOGLE_OAUTH_MODEL_STORAGE` in
settings.py. See "Adding Credentials To An Existing Django User System" for
usage differences.

Only Django versions 1.8+ are supported.

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

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

Add the helper to your INSTALLED_APPS:

.. code-block:: python
   :caption: settings.py
   :name: installed_apps

    INSTALLED_APPS = (
        # other apps
        "django.contrib.sessions.middleware"
        "oauth2client.contrib.django_util"
    )

This helper also requires the Django Session Middleware, so
``django.contrib.sessions.middleware`` should be in INSTALLED_APPS as well.

Add the client secrets created earlier to the settings. You can either
specify the path to the credentials file in JSON format

.. code-block:: python
   :caption:  settings.py
   :name: secrets_file

   GOOGLE_OAUTH2_CLIENT_SECRETS_JSON=/path/to/client-secret.json

Or, directly configure the client Id and client secret.


.. code-block:: python
   :caption: settings.py
   :name: secrets_config

   GOOGLE_OAUTH2_CLIENT_ID=client-id-field
   GOOGLE_OAUTH2_CLIENT_SECRET=client-secret-field

By default, the default scopes for the required decorator only contains the
``email`` scopes. You can change that default in the settings.

.. code-block:: python
   :caption: settings.py
   :name: scopes

   GOOGLE_OAUTH2_SCOPES = ('email', 'https://www.googleapis.com/auth/calendar',)

By default, the decorators will add an `oauth` object to the Django request
object, and include all of its state and helpers inside that object. If the
`oauth` name conflicts with another usage, it can be changed

.. code-block:: python
   :caption: settings.py
   :name: request_prefix

   # changes request.oauth to request.google_oauth
   GOOGLE_OAUTH2_REQUEST_ATTRIBUTE = 'google_oauth'

Add the oauth2 routes to your application's urls.py urlpatterns.

.. code-block:: python
   :caption: urls.py
   :name: urls

   from oauth2client.contrib.django_util.site import urls as oauth2_urls

   urlpatterns += [url(r'^oauth2/', include(oauth2_urls))]

To require OAuth2 credentials for a view, use the `oauth2_required` decorator.
This creates a credentials object with an id_token, and allows you to create
an `http` object to build service clients with. These are all attached to the
request.oauth

.. code-block:: python
   :caption: views.py
   :name: views_required

   from oauth2client.contrib.django_util.decorators import oauth_required

   @oauth_required
   def requires_default_scopes(request):
      email = request.oauth.credentials.id_token['email']
      service = build(serviceName='calendar', version='v3',
                    http=request.oauth.http,
                   developerKey=API_KEY)
      events = service.events().list(calendarId='primary').execute()['items']
      return HttpResponse("email: {0} , calendar: {1}".format(
                           email,str(events)))
      return HttpResponse(
          "email: {0} , calendar: {1}".format(email, str(events)))

To make OAuth2 optional and provide an authorization link in your own views.

.. code-block:: python
   :caption: views.py
   :name: views_enabled2

   from oauth2client.contrib.django_util.decorators import oauth_enabled

   @oauth_enabled
   def optional_oauth2(request):
       if request.oauth.has_credentials():
           # this could be passed into a view
           # request.oauth.http is also initialized
           return HttpResponse("User email: {0}".format(
               request.oauth.credentials.id_token['email']))
       else:
           return HttpResponse(
               'Here is an OAuth Authorize link: <a href="{0}">Authorize'
               '</a>'.format(request.oauth.get_authorize_redirect()))

If a view needs a scope not included in the default scopes specified in
the settings, you can use [incremental auth](https://developers.google.com/identity/sign-in/web/incremental-auth)
and specify additional scopes in the decorator arguments.

.. code-block:: python
   :caption: views.py
   :name: views_required_additional_scopes

   @oauth_enabled(scopes=['https://www.googleapis.com/auth/drive'])
   def drive_required(request):
       if request.oauth.has_credentials():
           service = build(serviceName='drive', version='v2',
                http=request.oauth.http,
                developerKey=API_KEY)
           events = service.files().list().execute()['items']
           return HttpResponse(str(events))
       else:
           return HttpResponse(
               'Here is an OAuth Authorize link: <a href="{0}">Authorize'
               '</a>'.format(request.oauth.get_authorize_redirect()))


To provide a callback on authorization being completed, use the
oauth2_authorized signal:

.. code-block:: python
   :caption: views.py
   :name: signals

   from oauth2client.contrib.django_util.signals import oauth2_authorized

   def test_callback(sender, request, credentials, **kwargs):
       print("Authorization Signal Received {0}".format(
               credentials.id_token['email']))

   oauth2_authorized.connect(test_callback)

Adding Credentials To An Existing Django User System
=====================================================

As an alternative to storing the credentials in the session, the helper
can be configured to store the fields on a Django model. This might be useful
if you need to use the credentials outside the context of a user request. It
also prevents the need for a logged in user to repeat the OAuth flow when
starting a new session.

To use, change ``settings.py``

.. code-block:: python
   :caption:  settings.py
   :name: storage_model_config

   GOOGLE_OAUTH2_STORAGE_MODEL = {
       'model': 'path.to.model.MyModel',
       'user_property': 'user_id',
       'credentials_property': 'credential'
    }

Where ``path.to.model`` class is the fully qualified name of a
``django.db.model`` class containing a ``django.contrib.auth.models.User``
field with the name specified by `user_property` and a
:class:`oauth2client.contrib.django_util.models.CredentialsField` with the name
specified by `credentials_property`. For the sample configuration given,
our model would look like

.. code-block:: python
   :caption: models.py
   :name: storage_model_model

   from django.contrib.auth.models import User
   from oauth2client.contrib.django_util.models import CredentialsField

   class MyModel(models.Model):
       #  ... other fields here ...
       user = models.OneToOneField(User)
       credential = CredentialsField()
    N)
exceptions)urlresolvers)parse)clientsecrets)dictionary_storage)storage)emailoauthc                     [         R                  " U 5      u  pU[         R                  :w  a  [        SR	                  U5      5      eUS   US   4$ )zLoads client secrets from the given filename.

Args:
    filename: The name of the file containing the JSON secret key.

Returns:
    A 2-tuple, the first item containing the client id, and the second
    item containing a client secret.
zPThe flow specified in {} is not supported, only the WEB flow type  is supported.	client_idclient_secret)r   loadfileTYPE_WEB
ValueErrorformat)filenameclient_typeclient_infos      <lib/third_party/oauth2client/contrib/django_util/__init__.py_load_client_secretsr      sX      -55h?Km,,,""(&"57 	7 {#[%AAA    c                     [        U SS5      nUb  [        U5      $ [        U SS5      n[        U SS5      nUb  Ub  X#4$ [        R                  " S5      e)zInitializes client id and client secret based on the settings.

Args:
    settings_instance: An instance of ``django.conf.settings``.

Returns:
    A 2-tuple, the first item is the client id and the second
     item is the client secret.
!GOOGLE_OAUTH2_CLIENT_SECRETS_JSONNGOOGLE_OAUTH2_CLIENT_IDGOOGLE_OAUTH2_CLIENT_SECRETzMust specify either GOOGLE_OAUTH2_CLIENT_SECRETS_JSON, or both GOOGLE_OAUTH2_CLIENT_ID and GOOGLE_OAUTH2_CLIENT_SECRET in settings.py)getattrr   r   ImproperlyConfigured)settings_instancesecret_jsonr   r   s       r    _get_oauth2_client_id_and_secretr      sz     +=tEK#K00-/H "	 1 =tE ]%>++11=> >r   c                  p    [        [        R                  R                  SS5      n U b  U S   U S   U S   4$ g)a  This configures whether the credentials will be stored in the session
or the Django ORM based on the settings. By default, the credentials
will be stored in the session, unless `GOOGLE_OAUTH2_STORAGE_MODEL`
is found in the settings. Usually, the ORM storage is used to integrate
credentials into an existing Django user system.

Returns:
    A tuple containing three strings, or None. If
    ``GOOGLE_OAUTH2_STORAGE_MODEL`` is configured, the tuple
    will contain the fully qualifed path of the `django.db.model`,
    the name of the ``django.contrib.auth.models.User`` field on the
    model, and the name of the
    :class:`oauth2client.contrib.django_util.models.CredentialsField`
    field on the model. If Django ORM storage is not configured,
    this function returns None.
GOOGLE_OAUTH2_STORAGE_MODELNmodeluser_propertycredentials_property)NNN)r   djangoconfsettings)storage_model_settingss    r   _get_storage_modelr*   !  sQ    " %V[[%9%9%BDJ)&w/&7&'=>@ 	@  r   c                       \ rS rSrSrS rSrg)OAuth2Settingsi<  a  Initializes Django OAuth2 Helper Settings

This class loads the OAuth2 Settings from the Django settings, and then
provides those settings as attributes to the rest of the views and
decorators in the module.

Attributes:
  scopes: A list of OAuth2 scopes that the decorators and views will use
          as defaults.
  request_prefix: The name of the attribute that the decorators use to
                attach the UserOAuth2 object to the Django request object.
  client_id: The OAuth2 Client ID.
  client_secret: The OAuth2 Client Secret.
c                    [        US[        5      U l        [        US[        5      U l        [        U5      u  U l        U l        SUR                  ;  a  [        R                  " S5      e[        5       u  U l        U l        U l        g )NGOOGLE_OAUTH2_SCOPESGOOGLE_OAUTH2_REQUEST_ATTRIBUTEz4django.contrib.sessions.middleware.SessionMiddlewarezThe Google OAuth2 Helper requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to include 'django.contrib.sessions.middleware.SessionMiddleware'.)r   GOOGLE_OAUTH2_DEFAULT_SCOPESscopesr/   request_prefixr    r   r   MIDDLEWARE_CLASSESr   r   r*   storage_modelstorage_model_user_property"storage_model_credentials_property)selfr   s     r   __init__OAuth2Settings.__init__L  s    /1G:<%&7&G&EG -->? 	+* C#66711)* * 5G4H	2	T=		0r   )r   r   r2   r1   r4   r6   r5   N)__name__
__module____qualname____firstlineno____doc__r8   __static_attributes__ r   r   r,   r,   <  s    Ir   r,   google_oauth2_credentialsc                 f   [         R                  n[         R                  n[         R                  nU(       aX  UR	                  SS5      u  pE[
        R                  " U5      n[        Xe5      n[        R                  " UUU R                  U5      $ [        R                  " U R                  [        S9$ )zGets a Credentials storage object provided by the Django OAuth2 Helper
object.

Args:
    request: Reference to the current request object.

Returns:
   An :class:`oauth2.client.Storage` object.
.   )key)oauth2_settingsr4   r5   r6   rsplit	importlibimport_moduler   r   DjangoORMStorageuserr   DictionaryStoragesession_CREDENTIALS_KEY)requestr4   r$   r%   module_name
class_namemodulestorage_model_classs           r   get_storagerT   e  s     $11M#??M*MM"/"6"6sA">((5%f9''(;(5(/(<> 	> "33OO!13 	3r   c                 z    [         R                  " XS9n[        R                  " US5      nSR	                  X45      $ )a1  Helper method to create a redirect response with URL params.

This builds a redirect string that converts kwargs into a
query string.

Args:
    url_name: The name of the url to redirect to.
    kwargs: the query string param and their values to build.

Returns:
    A properly formatted redirect string.
)argsTz{0}?{1})r   reverser   	urlencoder   )url_namerV   kwargsurlparamss        r   _redirect_with_paramsr]     s5     

x
3C__VT*FC((r   c                     [         R                  b  U R                  R                  5       (       a  [	        U 5      R                  5       $ g)=Gets the authorized credentials for this flow, if they exist.N)rF   r4   rK   is_authenticatedrT   get)rO   s    r   _credentials_from_requestrb     s:     	%%-LL))++7#''))r   c                   d    \ rS rSrSrSS jrS rS rS r\	S 5       r
\	S	 5       r\	S
 5       rSrg)
UserOAuth2i  zdClass to create oauth2 objects on Django request objects containing
credentials and helper methods.
Nc                     Xl         U=(       d    UR                  5       U l        U(       a+  [        [        R
                  5      [        U5      -  U l        g[        [        R
                  5      U l        g)zInitialize the Oauth2 Object.

Args:
    request: Django request object.
    scopes: Scopes desired for this OAuth2 flow.
    return_url: The url to return to after the OAuth flow is complete,
         defaults to the request's current URL path.
N)rO   get_full_path
return_urlsetrF   r1   _scopes)r7   rO   r1   rg   s       r   r8   UserOAuth2.__init__  sN     $?(=(=(?556VDDL556DLr   c                 R    U R                   U R                  5       S.n[        S0 UD6$ )z5Creates a URl to start the OAuth2 authorization flow.)rg   r1   )zgoogle_oauth:authorize)rg   _get_scopesr]   )r7   
get_paramss     r   get_authorize_redirect!UserOAuth2.get_authorize_redirect  s.     //&&(


 %LLLr   c                     [        U R                  5      nU=(       a7    UR                  (       + =(       a    UR                  U R	                  5       5      $ )zUReturns True if there are valid credentials for the current user
and required scopes.)rb   rO   invalid
has_scopesrl   )r7   credentialss     r   has_credentialsUserOAuth2.has_credentials  sF     0= ;K$7$7 7 ;&&t'7'7'9:	<r   c                     [        U R                  5      (       a,  U R                  [        U R                  5      R                  -  $ U R                  $ )zUReturns the scopes associated with this object, kept up to
date for incremental auth.)rb   rO   ri   r1   r7   s    r   rl   UserOAuth2._get_scopes  sD     %T\\22LL-dll;BBC D <<r   c                 "    U R                  5       $ )z6Returns the scopes associated with this OAuth2 object.)rl   rw   s    r   r1   UserOAuth2.scopes  s    
 !!r   c                 ,    [        U R                  5      $ )r_   )rb   rO   rw   s    r   rs   UserOAuth2.credentials  s     )66r   c                     U R                  5       (       a.  U R                  R                  [        R                  " 5       5      $ g)zJHelper method to create an HTTP client authorized with OAuth2
credentials.N)rt   rs   	authorizehttplib2Httprw   s    r   httpUserOAuth2.http  s3     !!##--hmmo>>r   )ri   rO   rg   )NN)r:   r;   r<   r=   r>   r8   rn   rt   rl   propertyr1   rs   r   r?   r@   r   r   rd   rd     sX    7 M<  " " 7 7  r   rd   )r>   rH   django.confr&   django.corer   r   r   six.moves.urllibr   oauth2clientr   oauth2client.contribr    oauth2client.contrib.django_utilr   r0   r/   r   r    r*   objectr,   r'   r(   rF   rN   rT   r]   rb   rd   r@   r   r   <module>r      s   Qf   " $  " & 3 4) ") B&>8 6!IV !IH !!5!56. 38)$@ @r   