[Python-modules-commits] [django-oauth-toolkit] 03/09: Import django-oauth-toolkit_0.10.0.orig.tar.gz

Michael Fladischer fladi at moszumanska.debian.org
Fri Dec 25 13:53:54 UTC 2015


This is an automated email from the git hooks/post-receive script.

fladi pushed a commit to branch master
in repository django-oauth-toolkit.

commit 1f492d07e26344781bce98475d85d8928d8b2341
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Fri Dec 25 14:06:24 2015 +0100

    Import django-oauth-toolkit_0.10.0.orig.tar.gz
---
 .gitignore                                         |   1 +
 .travis.yml                                        |  24 +--
 AUTHORS                                            |   3 +
 README.rst                                         |  20 ++-
 docs/changelog.rst                                 |  20 ++-
 docs/index.rst                                     |   5 +-
 docs/install.rst                                   |   1 -
 docs/management_commands.rst                       |  20 +++
 docs/rest-framework/permissions.rst                |  15 ++
 docs/settings.rst                                  |  21 ++-
 docs/tutorial/tutorial_01.rst                      |  22 +--
 docs/tutorial/tutorial_03.rst                      |   6 +-
 docs/tutorial/tutorial_04.rst                      |   2 +-
 docs/views/token.rst                               |  15 ++
 docs/views/views.rst                               |   1 +
 oauth2_provider/__init__.py                        |   4 +-
 oauth2_provider/apps.py                            |   6 +
 oauth2_provider/compat.py                          |   6 +
 oauth2_provider/compat_handlers.py                 |   5 +
 oauth2_provider/ext/rest_framework/__init__.py     |   2 +-
 oauth2_provider/ext/rest_framework/permissions.py  |  25 +++
 oauth2_provider/forms.py                           |  13 +-
 oauth2_provider/management/__init__.py             |   0
 oauth2_provider/management/commands/__init__.py    |   0
 oauth2_provider/management/commands/cleartokens.py |   9 ++
 oauth2_provider/migrations/0001_initial.py         |   1 +
 oauth2_provider/models.py                          |  38 ++++-
 oauth2_provider/oauth2_backends.py                 |  21 ++-
 oauth2_provider/oauth2_validators.py               |   8 +-
 oauth2_provider/settings.py                        |  20 ++-
 .../application_confirm_delete.html                |   2 +-
 .../oauth2_provider/application_detail.html        |   2 +-
 .../oauth2_provider/application_form.html          |   2 +-
 .../oauth2_provider/application_list.html          |   2 +-
 .../application_registration_form.html             |   2 +-
 .../oauth2_provider/authorized-token-delete.html   |   9 ++
 .../oauth2_provider/authorized-tokens.html         |  24 +++
 oauth2_provider/templatetags/__init__.py           |   0
 oauth2_provider/templatetags/compat.py             |  10 ++
 oauth2_provider/tests/settings.py                  |   2 +-
 oauth2_provider/tests/test_application_views.py    |  20 +++
 oauth2_provider/tests/test_auth_backends.py        |   2 +-
 oauth2_provider/tests/test_authorization_code.py   |  32 ++++
 oauth2_provider/tests/test_client_credential.py    |   1 +
 oauth2_provider/tests/test_implicit.py             |  21 +++
 oauth2_provider/tests/test_models.py               |  40 +++++
 oauth2_provider/tests/test_oauth2_backends.py      |  35 ++++
 oauth2_provider/tests/test_password.py             |   1 +
 oauth2_provider/tests/test_rest_framework.py       |  51 +++++-
 oauth2_provider/tests/test_scopes.py               |   1 +
 oauth2_provider/tests/test_token_view.py           | 178 +++++++++++++++++++++
 oauth2_provider/tests/urls.py                      |   5 +-
 oauth2_provider/urls.py                            |  14 +-
 oauth2_provider/views/__init__.py                  |   1 +
 oauth2_provider/views/application.py               |  13 +-
 oauth2_provider/views/base.py                      |   8 +-
 oauth2_provider/views/generic.py                   |   4 +-
 oauth2_provider/views/token.py                     |  36 +++++
 requirements/base.txt                              |   7 +-
 requirements/optional.txt                          |   2 +-
 requirements/project.txt                           |   2 +-
 requirements/testing.txt                           |   7 +-
 runtests.py                                        |  10 +-
 setup.py                                           |  14 +-
 tox.ini                                            | 124 ++------------
 65 files changed, 806 insertions(+), 212 deletions(-)

diff --git a/.gitignore b/.gitignore
index 227bc50..bf1a049 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@ __pycache__
 pip-log.txt
 
 # Unit test / coverage reports
+.cache
 .coverage
 .tox
 nosetests.xml
diff --git a/.travis.yml b/.travis.yml
index bfbfb94..b344a75 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,25 +1,29 @@
 language: python
 python: "2.7"
+sudo: false
 
 env:
-  - TOX_ENV=py26-django14
-  - TOX_ENV=py26-django15
-  - TOX_ENV=py26-django16
-  - TOX_ENV=py27-django14
-  - TOX_ENV=py27-django15
-  - TOX_ENV=py27-django16
   - TOX_ENV=py27-django17
   - TOX_ENV=py27-django18
-  - TOX_ENV=py33-django15
-  - TOX_ENV=py33-django16
+  - TOX_ENV=py27-django19
+  - TOX_ENV=py32-django17
+  - TOX_ENV=py32-django18
   - TOX_ENV=py33-django17
   - TOX_ENV=py33-django18
-  - TOX_ENV=py34-django15
-  - TOX_ENV=py34-django16
   - TOX_ENV=py34-django17
   - TOX_ENV=py34-django18
+  - TOX_ENV=py34-django19
+  - TOX_ENV=py35-django18
+  - TOX_ENV=py35-django19
   - TOX_ENV=docs
 
+matrix:
+  # Python 3.5 not yet available on travis, watch this to see when it is.
+  fast_finish: true
+  allow_failures:
+    - env: TOX_ENV=py35-django18
+    - env: TOX_ENV=py35-django19
+
 install:
   - pip install tox
   - pip install coveralls
diff --git a/AUTHORS b/AUTHORS
index 8fa585f..7b2c6b8 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -13,3 +13,6 @@ David Fischer
 Ash Christopher
 Rodney Richardson
 Hiroki Kiyohara
+Diego Garcia
+Bas van Oostveen
+Bart Merenda
diff --git a/README.rst b/README.rst
index 42dec11..5eed1ba 100644
--- a/README.rst
+++ b/README.rst
@@ -37,8 +37,8 @@ guidelines <https://django-oauth-toolkit.readthedocs.org/en/latest/contributing.
 Requirements
 ------------
 
-* Python 2.6, 2.7, 3.3, 3.4
-* Django 1.4, 1.5, 1.6, 1.7, 1.8
+* Python 2.7, 3.2, 3.3, 3.4, 3.5
+* Django 1.7, 1.8, 1.9
 
 Installation
 ------------
@@ -87,6 +87,22 @@ Roadmap / Todo list (help wanted)
 Changelog
 ---------
 
+0.10.0 [2015-12-14]
+~~~~~~~~~~~~~~~~~~~
+
+* **#322: dropping support for python 2.6 and django 1.4, 1.5, 1.6**
+* #310: Fixed error that could occur sometimes when checking validity of incomplete AccessToken/Grant
+* #333: Added possibility to specify the default list of scopes returned when scope parameter is missing
+* #325: Added management views of issued tokens
+* #249: Added a command to clean expired tokens
+* #323: Application registration view uses custom application model in form class
+* #299: 'server_class' is now pluggable through Django settings
+* #309: Add the py35-django19 env to travis
+* #308: Use compact syntax for tox envs
+* #306: Django 1.9 compatibility
+* #288: Put additional information when generating token responses
+* #297: Fixed doc about SessionAuthenticationMiddleware
+* #273: Generic read write scope by resource
 
 0.9.0 [2015-07-28]
 ~~~~~~~~~~~~~~~~~~
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 839a34e..a9a4e5a 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,8 +1,26 @@
 Changelog
 =========
 
+0.10.0 [2015-12-14]
+------------------
+
+* **#322: dropping support for python 2.6 and django 1.4, 1.5, 1.6**
+* #310: Fixed error that could occur sometimes when checking validity of incomplete AccessToken/Grant
+* #333: Added possibility to specify the default list of scopes returned when scope parameter is missing
+* #325: Added management views of issued tokens
+* #249: Added a command to clean expired tokens
+* #323: Application registration view uses custom application model in form class
+* #299: 'server_class' is now pluggable through Django settings
+* #309: Add the py35-django19 env to travis
+* #308: Use compact syntax for tox envs
+* #306: Django 1.9 compatibility
+* #288: Put additional information when generating token responses
+* #297: Fixed doc about SessionAuthenticationMiddleware
+* #273: Generic read write scope by resource
+
+
 0.9.0 [2015-07-28]
-~~~~~~~~~~~~~~~~~~
+------------------
 
 * ``oauthlib_backend_class`` is now pluggable through Django settings
 * #127: ``application/json`` Content-Type is now supported using ``JSONOAuthLibCore``
diff --git a/docs/index.rst b/docs/index.rst
index 4aa62ff..de2c0f8 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -21,8 +21,8 @@ If you need support please send a message to the `Django OAuth Toolkit Google Gr
 Requirements
 ------------
 
-* Python 2.6, 2.7, 3.3, 3.4
-* Django 1.4, 1.5, 1.6, 1.7
+* Python 2.7, 3.2, 3.3, 3.4, 3.5
+* Django 1.7, 1.8, 1.9
 
 Index
 =====
@@ -38,6 +38,7 @@ Index
    models
    advanced_topics
    settings
+   management_commands
    glossary
 
 .. toctree::
diff --git a/docs/install.rst b/docs/install.rst
index efc21e9..adaf95f 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -29,7 +29,6 @@ Sync your database
 
 .. sourcecode:: sh
 
-    $ python manage.py syncdb
     $ python manage.py migrate oauth2_provider
 
 Next step is our :doc:`first tutorial <tutorial/tutorial_01>`.
diff --git a/docs/management_commands.rst b/docs/management_commands.rst
new file mode 100644
index 0000000..3930062
--- /dev/null
+++ b/docs/management_commands.rst
@@ -0,0 +1,20 @@
+Management commands
+===================
+
+Django OAuth Toolkit exposes some useful management commands that can be run via shell or by other means (eg: cron)
+
+.. _cleartokens:
+
+cleartokens
+~~~~~~~~~~~
+
+The ``cleartokens`` management command allows the user to remove those refresh tokens whose lifetime is greater than the
+amount specified by ``REFRESH_TOKEN_EXPIRE_SECONDS`` settings. It is important that this command is run regularly
+(eg: via cron) to avoid cluttering the database with expired refresh tokens.
+
+If ``cleartokens`` runs daily the maximum delay before a refresh token is
+removed is ``REFRESH_TOKEN_EXPIRE_SECONDS`` + 1 day. This is normally not a
+problem since refresh tokens are long lived.
+
+Note: Refresh tokens need to expire before AccessTokens can be removed from the
+database. Using ``cleartokens`` without ``REFRESH_TOKEN_EXPIRE_SECONDS`` has limited effect.
diff --git a/docs/rest-framework/permissions.rst b/docs/rest-framework/permissions.rst
index 092d580..d22e8f4 100644
--- a/docs/rest-framework/permissions.rst
+++ b/docs/rest-framework/permissions.rst
@@ -48,3 +48,18 @@ For example:
 
 When a request is performed both the `READ_SCOPE` \\ `WRITE_SCOPE` and 'music' scopes are required to be authorized for the current access token.
 
+TokenHasResourceScope
+----------------------
+The `TokenHasResourceScope` permission class allows the access only when the current access token has been authorized for **all** the scopes listed in the `required_scopes` field of the view but according of request's method.
+
+When the current request's method is one of the "safe" methods, the access is allowed only if the access token has been authorized for the `scope:read` scope (for example `music:read`).
+When the request's method is one of "non safe" methods, the access is allowed only if the access token has been authorizes for the `scope:write` scope (for example `music:write`).
+
+.. code-block:: python
+
+    class SongView(views.APIView):
+        authentication_classes = [OAuth2Authentication]
+        permission_classes = [TokenHasResourceScope]
+        required_scopes = ['music']
+
+The `required_scopes` attribute is mandatory (you just need inform the resource scope).
diff --git a/docs/settings.rst b/docs/settings.rst
index 6fc46da..4999c1c 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -59,6 +59,11 @@ CLIENT_SECRET_GENERATOR_LENGTH
 The length of the generated secrets, in characters. If this value is too low,
 secrets may become subject to bruteforce guessing.
 
+OAUTH2_SERVER_CLASS
+~~~~~~~~~~~~~~~~~~~~
+The import string for the ``server_class`` (or ``oauthlib.oauth2.Server`` subclass)
+used in the ``OAuthLibMixin`` that implements OAuth2 grant types.
+
 OAUTH2_VALIDATOR_CLASS
 ~~~~~~~~~~~~~~~~~~~~~~
 The import string of the ``oauthlib.oauth2.RequestValidator`` subclass that
@@ -71,7 +76,16 @@ to get a ``Server`` instance.
 
 SCOPES
 ~~~~~~
-A dictionnary mapping each scope name to its human description.
+A dictionary mapping each scope name to its human description.
+
+DEFAULT_SCOPES
+~~~~~~~~~~~~~~
+A list of scopes that should be returned by default.
+This is a subset of the keys of the SCOPES setting.
+By default this is set to '__all__' meaning that the whole set of SCOPES will be returned.
+.. code-block:: python
+
+  DEFAULT_SCOPES = ['read', 'write']
 
 READ_SCOPE
 ~~~~~~~~~~
@@ -81,6 +95,11 @@ WRITE_SCOPE
 ~~~~~~~~~~~
 The name of the *write* scope.
 
+REFRESH_TOKEN_EXPIRE_SECONDS
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The number of seconds before a refresh token gets removed from the database by
+the ``cleartokens`` management command. Check :ref:`cleartokens` management command for further info.
+
 REQUEST_APPROVAL_PROMPT
 ~~~~~~~~~~~~~~~~~~~~~~~
 Can be ``'force'`` or ``'auto'``.
diff --git a/docs/tutorial/tutorial_01.rst b/docs/tutorial/tutorial_01.rst
index c7c6687..fdb1c3e 100644
--- a/docs/tutorial/tutorial_01.rst
+++ b/docs/tutorial/tutorial_01.rst
@@ -8,7 +8,7 @@ You want to make your own :term:`Authorization Server` to issue access tokens to
 Start Your App
 --------------
 During this tutorial you will make an XHR POST from a Heroku deployed app to your localhost instance.
-Since the domain that will originate the request (the app on Heroku) is different than the destination domain (your local instance), 
+Since the domain that will originate the request (the app on Heroku) is different than the destination domain (your local instance),
 you will need to install the `django-cors-headers <https://github.com/ottoyiu/django-cors-headers>`_ app.
 These "cross-domain" requests are by default forbidden by web browsers unless you use `CORS <http://en.wikipedia.org/wiki/Cross-origin_resource_sharing>`_.
 
@@ -60,29 +60,29 @@ Allow CORS requests from all domains (just for the scope of this tutorial):
 
 Include the required hidden input in your login template, `registration/login.html`.
 The ``{{ next }}`` template context variable will be populated with the correct
-redirect value. See the `Django documentation <https://docs.djangoproject.com/en/dev/topics/auth/default/#django.contrib.auth.views.login>`_ 
+redirect value. See the `Django documentation <https://docs.djangoproject.com/en/dev/topics/auth/default/#django.contrib.auth.views.login>`_
 for details on using login templates.
 
 .. code-block:: html
 
     <input type="hidden" name="next" value="{{ next }}" />
 
-As a final step, execute syncdb, start the internal server, and login with your credentials.
+As a final step, execute migrate command, start the internal server, and login with your credentials.
 
 Create an OAuth2 Client Application
 -----------------------------------
-Before your :term:`Application` can use the :term:`Authorization Server` for user login, 
-you must first register the app (also known as the :term:`Client`.) Once registered, your app will be granted access to 
+Before your :term:`Application` can use the :term:`Authorization Server` for user login,
+you must first register the app (also known as the :term:`Client`.) Once registered, your app will be granted access to
 the API, subject to approval by its users.
 
-Let's register your application. 
+Let's register your application.
 
 Point your browser to http://localhost:8000/o/applications/ and add an Application instance.
 `Client id` and `Client Secret` are automatically generated, you have to provide the rest of the informations:
 
  * `User`: the owner of the Application (e.g. a developer, or the currently logged in user.)
 
- * `Redirect uris`: Applications must register at least one redirection endpoint prior to utilizing the 
+ * `Redirect uris`: Applications must register at least one redirection endpoint prior to utilizing the
    authorization endpoint. The :term:`Authorization Server` will deliver the access token to the client only if the client
    specifies one of the verified redirection uris. For this tutorial, paste verbatim the value
    `http://django-oauth-toolkit.herokuapp.com/consumer/exchange/`
@@ -101,16 +101,16 @@ process we'll explain shortly)
 Test Your Authorization Server
 ------------------------------
 Your authorization server is ready and can begin issuing access tokens. To test the process you need an OAuth2
-consumer; if you are familiar enough with OAuth2, you can use curl, requests, or anything that speaks http. For the rest 
-of us, there is a `consumer service <http://django-oauth-toolkit.herokuapp.com/consumer/>`_ deployed on Heroku to test 
+consumer; if you are familiar enough with OAuth2, you can use curl, requests, or anything that speaks http. For the rest
+of us, there is a `consumer service <http://django-oauth-toolkit.herokuapp.com/consumer/>`_ deployed on Heroku to test
 your provider.
 
 Build an Authorization Link for Your Users
 ++++++++++++++++++++++++++++++++++++++++++
 Authorizing an application to access OAuth2 protected data in an :term:`Authorization Code` flow is always initiated
-by the user. Your application can prompt users to click a special link to start the process. Go to the 
+by the user. Your application can prompt users to click a special link to start the process. Go to the
 `Consumer <http://django-oauth-toolkit.herokuapp.com/consumer/>`_ page and complete the form by filling in your
-application's details obtained from the steps in this tutorial. Submit the form, and you'll receive a link your users can 
+application's details obtained from the steps in this tutorial. Submit the form, and you'll receive a link your users can
 use to access the authorization page.
 
 Authorize the Application
diff --git a/docs/tutorial/tutorial_03.rst b/docs/tutorial/tutorial_03.rst
index 612414c..210cc24 100644
--- a/docs/tutorial/tutorial_03.rst
+++ b/docs/tutorial/tutorial_03.rst
@@ -24,7 +24,8 @@ which takes care of token verification. In your settings.py:
 
     MIDDLEWARE_CLASSES = (
         '...',
-        # be sure following two appear in this order
+        # If you use SessionAuthenticationMiddleware, be sure it appears before OAuth2TokenMiddleware.
+        # SessionAuthenticationMiddleware is NOT required for using django-oauth-toolkit.
         'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
         'oauth2_provider.middleware.OAuth2TokenMiddleware',
         '...',
@@ -43,6 +44,9 @@ not used at all, it will try to authenticate user with the OAuth2 access token a
 `request.user` and `request._cached_user` fields so that AuthenticationMiddleware (when active)
 will not try to get user from the session.
 
+If you use SessionAuthenticationMiddleware, be sure it appears before OAuth2TokenMiddleware.
+However SessionAuthenticationMiddleware is NOT required for using django-oauth-toolkit.
+
 Protect your view
 -----------------
 The authentication backend will run smoothly with, for example, `login_required` decorators, so
diff --git a/docs/tutorial/tutorial_04.rst b/docs/tutorial/tutorial_04.rst
index e062c1a..e115f82 100644
--- a/docs/tutorial/tutorial_04.rst
+++ b/docs/tutorial/tutorial_04.rst
@@ -18,7 +18,7 @@ Note that these revocation-specific parameters are in addition to the authentica
 
 Setup a Request
 ----------------
-Depending on the client type you're using, the token revocation request you may submit to the authentication server mayy vary. A `Public` client, for example, will not have access to your `Client Secret`. A revoke request from a public client would omit that secret, and take the form:
+Depending on the client type you're using, the token revocation request you may submit to the authentication server may vary. A `Public` client, for example, will not have access to your `Client Secret`. A revoke request from a public client would omit that secret, and take the form:
 
 ::
 
diff --git a/docs/views/token.rst b/docs/views/token.rst
new file mode 100644
index 0000000..02f6bf5
--- /dev/null
+++ b/docs/views/token.rst
@@ -0,0 +1,15 @@
+Granted Tokens Views
+====================
+
+A set of views is provided to let users handle tokens that have been granted to them, without needing to accessing Django Admin Site.
+Every view provides access only to the tokens that have been granted to the user performing the request.
+
+
+Granted Token views are listed at the url `authorized_tokens/`.
+
+
+For each granted token there is a delete view that allows you to delete such token. You can override default templates `authorized-tokens.html` for the list view and `authorized-token-delete.html` for the delete view; they are located inside `templates/oauth2_provider` folder. 
+
+
+.. automodule:: oauth2_provider.views.token
+    :members:
diff --git a/docs/views/views.rst b/docs/views/views.rst
index 34afef9..262f9d2 100644
--- a/docs/views/views.rst
+++ b/docs/views/views.rst
@@ -9,4 +9,5 @@ Django OAuth Toolkit provides a set of pre-defined views for different purposes:
    function_based
    class_based
    application
+   token
    mixins
diff --git a/oauth2_provider/__init__.py b/oauth2_provider/__init__.py
index 9ffed34..a13af83 100644
--- a/oauth2_provider/__init__.py
+++ b/oauth2_provider/__init__.py
@@ -1,5 +1,7 @@
-__version__ = '0.9.0'
+__version__ = '0.10.0'
 
 __author__ = "Massimiliano Pippi & Federico Frenguelli"
 
+default_app_config = 'oauth2_provider.apps.DOTConfig'
+
 VERSION = __version__  # synonym
diff --git a/oauth2_provider/apps.py b/oauth2_provider/apps.py
new file mode 100644
index 0000000..6f67f38
--- /dev/null
+++ b/oauth2_provider/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class DOTConfig(AppConfig):
+    name = 'oauth2_provider'
+    verbose_name = "Django OAuth Toolkit"
diff --git a/oauth2_provider/compat.py b/oauth2_provider/compat.py
index 4266c34..3fca936 100644
--- a/oauth2_provider/compat.py
+++ b/oauth2_provider/compat.py
@@ -37,3 +37,9 @@ try:
     get_model = apps.get_model
 except ImportError:
     from django.db.models import get_model
+
+# Django 1.5 add the support of context variables for the url template tag
+if django.VERSION >= (1, 5):
+    from django.template.defaulttags import url
+else:
+    from django.templatetags.future import url
diff --git a/oauth2_provider/compat_handlers.py b/oauth2_provider/compat_handlers.py
new file mode 100644
index 0000000..21859e8
--- /dev/null
+++ b/oauth2_provider/compat_handlers.py
@@ -0,0 +1,5 @@
+# Django 1.9 drops the NullHandler since Python 2.7 includes it
+try:
+    from logging import NullHandler
+except ImportError:
+    from django.utils.log import NullHandler
diff --git a/oauth2_provider/ext/rest_framework/__init__.py b/oauth2_provider/ext/rest_framework/__init__.py
index 24cbda1..00da0a1 100644
--- a/oauth2_provider/ext/rest_framework/__init__.py
+++ b/oauth2_provider/ext/rest_framework/__init__.py
@@ -1,2 +1,2 @@
 from .authentication import OAuth2Authentication
-from .permissions import TokenHasScope, TokenHasReadWriteScope
+from .permissions import TokenHasScope, TokenHasReadWriteScope, TokenHasResourceScope
diff --git a/oauth2_provider/ext/rest_framework/permissions.py b/oauth2_provider/ext/rest_framework/permissions.py
index e60415d..559bbbc 100644
--- a/oauth2_provider/ext/rest_framework/permissions.py
+++ b/oauth2_provider/ext/rest_framework/permissions.py
@@ -59,3 +59,28 @@ class TokenHasReadWriteScope(TokenHasScope):
             read_write_scope = oauth2_settings.WRITE_SCOPE
 
         return required_scopes + [read_write_scope]
+
+
+class TokenHasResourceScope(TokenHasScope):
+    """
+    The request is authenticated as a user and the token used has the right scope
+    """
+
+    def get_scopes(self, request, view):
+        try:
+            view_scopes = (
+                super(TokenHasResourceScope, self).get_scopes(request, view)
+            )
+        except ImproperlyConfigured:
+            view_scopes = []
+
+        if request.method.upper() in SAFE_HTTP_METHODS:
+            scope_type = oauth2_settings.READ_SCOPE
+        else:
+            scope_type = oauth2_settings.WRITE_SCOPE
+
+        required_scopes = [
+            '{0}:{1}'.format(scope, scope_type) for scope in view_scopes
+        ]
+
+        return required_scopes
diff --git a/oauth2_provider/forms.py b/oauth2_provider/forms.py
index a8b3985..a2b4d8f 100644
--- a/oauth2_provider/forms.py
+++ b/oauth2_provider/forms.py
@@ -1,12 +1,10 @@
 from django import forms
 
-from .models import Application
-
 
 class AllowForm(forms.Form):
     allow = forms.BooleanField(required=False)
     redirect_uri = forms.CharField(widget=forms.HiddenInput())
-    scope = forms.CharField(required=False, widget=forms.HiddenInput())
+    scope = forms.CharField(widget=forms.HiddenInput())
     client_id = forms.CharField(widget=forms.HiddenInput())
     state = forms.CharField(required=False, widget=forms.HiddenInput())
     response_type = forms.CharField(widget=forms.HiddenInput())
@@ -17,12 +15,3 @@ class AllowForm(forms.Form):
         if data and 'scopes' in data:
             data['scope'] = data['scopes']
         return super(AllowForm, self).__init__(*args, **kwargs)
-
-
-class RegistrationForm(forms.ModelForm):
-    """
-    TODO: add docstring
-    """
-    class Meta:
-        model = Application
-        fields = ('name', 'client_id', 'client_secret', 'client_type', 'authorization_grant_type', 'redirect_uris')
diff --git a/oauth2_provider/management/__init__.py b/oauth2_provider/management/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/oauth2_provider/management/commands/__init__.py b/oauth2_provider/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/oauth2_provider/management/commands/cleartokens.py b/oauth2_provider/management/commands/cleartokens.py
new file mode 100644
index 0000000..5b56d2b
--- /dev/null
+++ b/oauth2_provider/management/commands/cleartokens.py
@@ -0,0 +1,9 @@
+from django.core.management.base import BaseCommand, CommandError
+from ...models import clear_expired
+
+
+class Command(BaseCommand):
+    help = "Can be run as a cronjob or directly to clean out expired tokens"
+
+    def handle(self, *args, **options):
+        clear_expired()
diff --git a/oauth2_provider/migrations/0001_initial.py b/oauth2_provider/migrations/0001_initial.py
index bb1b518..a1c59c7 100644
--- a/oauth2_provider/migrations/0001_initial.py
+++ b/oauth2_provider/migrations/0001_initial.py
@@ -30,6 +30,7 @@ class Migration(migrations.Migration):
             ],
             options={
                 'abstract': False,
+                'swappable': 'OAUTH2_PROVIDER_APPLICATION_MODEL',
             },
         ),
         migrations.CreateModel(
diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py
index 1d26726..fd3cdf4 100644
--- a/oauth2_provider/models.py
+++ b/oauth2_provider/models.py
@@ -1,7 +1,9 @@
 from __future__ import unicode_literals
 
+from datetime import timedelta
+
 from django.core.urlresolvers import reverse
-from django.db import models
+from django.db import models, transaction
 from django.utils import timezone
 
 from django.utils.translation import ugettext_lazy as _
@@ -155,6 +157,9 @@ class Grant(models.Model):
         """
         Check token expiration with timezone awareness
         """
+        if not self.expires:
+            return True
+
         return timezone.now() >= self.expires
 
     def redirect_uri_allowed(self, uri):
@@ -196,6 +201,9 @@ class AccessToken(models.Model):
         """
         Check token expiration with timezone awareness
         """
+        if not self.expires:
+            return True
+
         return timezone.now() >= self.expires
 
     def allow_scopes(self, scopes):
@@ -219,6 +227,13 @@ class AccessToken(models.Model):
         """
         self.delete()
 
+    @property
+    def scopes(self):
+        """
+        Returns a dictionary of allowed scope names (as keys) with their descriptions (as values)
+        """
+        return {name: desc for name, desc in oauth2_settings.SCOPES.items() if name in self.scope.split()}
+
     def __str__(self):
         return self.token
 
@@ -266,3 +281,24 @@ def get_application_model():
         e = "APPLICATION_MODEL refers to model {0} that has not been installed"
         raise ImproperlyConfigured(e.format(oauth2_settings.APPLICATION_MODEL))
     return app_model
+
+
+def clear_expired():
+    now = timezone.now()
+    refresh_expire_at = None
+
+    REFRESH_TOKEN_EXPIRE_SECONDS = oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS
+    if REFRESH_TOKEN_EXPIRE_SECONDS:
+        if not isinstance(REFRESH_TOKEN_EXPIRE_SECONDS, timedelta):
+            try:
+                REFRESH_TOKEN_EXPIRE_SECONDS = timedelta(seconds=REFRESH_TOKEN_EXPIRE_SECONDS)
+            except TypeError:
+                e = "REFRESH_TOKEN_EXPIRE_SECONDS must be either a timedelta or seconds"
+                raise ImproperlyConfigured(e)
+        refresh_expire_at = now - REFRESH_TOKEN_EXPIRE_SECONDS
+
+    with transaction.atomic():
+        if refresh_expire_at:
+            RefreshToken.objects.filter(access_token__expires__lt=refresh_expire_at).delete()
+        AccessToken.objects.filter(refresh_token__isnull=True, expires__lt=now).delete()
+        Grant.objects.filter(expires__lt=now).delete()
diff --git a/oauth2_provider/oauth2_backends.py b/oauth2_provider/oauth2_backends.py
index 4c7e40a..6fbdc82 100644
--- a/oauth2_provider/oauth2_backends.py
+++ b/oauth2_provider/oauth2_backends.py
@@ -18,7 +18,7 @@ class OAuthLibCore(object):
         """
         :params server: An instance of oauthlib.oauth2.Server class
         """
-        self.server = server or oauth2.Server(oauth2_settings.OAUTH2_VALIDATOR_CLASS())
+        self.server = server or oauth2_settings.OAUTH2_SERVER_CLASS(oauth2_settings.OAUTH2_VALIDATOR_CLASS())
 
     def _get_escaped_full_path(self, request):
         """
@@ -32,6 +32,17 @@ class OAuthLibCore(object):
 
         return urlunparse(parsed)
 
+    def _get_extra_credentials(self, request):
+        """
+        Produce extra credentials for token response. This dictionary will be
+        merged with the response.
+        See also: `oauthlib.oauth2.rfc6749.TokenEndpoint.create_token_response`
+
+        :param request: The current django.http.HttpRequest object
+        :return: dictionary of extra credentials or None (default)
+        """
+        return None
+
     def _extract_params(self, request):
         """
         Extract parameters from the Django request object. Such parameters will then be passed to
@@ -121,9 +132,10 @@ class OAuthLibCore(object):
         :param request: The current django.http.HttpRequest object
         """
         uri, http_method, body, headers = self._extract_params(request)
+        extra_credentials = self._get_extra_credentials(request)
 
         headers, body, status = self.server.create_token_response(uri, http_method, body,
-                                                                  headers)
+                                                                  headers, extra_credentials)
         uri = headers.get("Location", None)
 
         return uri, headers, body, status
@@ -179,7 +191,6 @@ def get_oauthlib_core():
     Utility function that take a request and returns an instance of
     `oauth2_provider.backends.OAuthLibCore`
     """
-    from oauthlib.oauth2 import Server
-
-    server = Server(oauth2_settings.OAUTH2_VALIDATOR_CLASS())
+    validator = oauth2_settings.OAUTH2_VALIDATOR_CLASS()
+    server = oauth2_settings.OAUTH2_SERVER_CLASS(validator)
     return oauth2_settings.OAUTH2_BACKEND_CLASS(server)
diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py
index cc669f0..25908d9 100644
--- a/oauth2_provider/oauth2_validators.py
+++ b/oauth2_provider/oauth2_validators.py
@@ -1,5 +1,6 @@
 from __future__ import unicode_literals
 
+import six
 import base64
 import binascii
 import logging
@@ -60,6 +61,11 @@ class OAuth2Validator(RequestValidator):
         except AttributeError:
             encoding = 'utf-8'
 
+        # Encode auth_string to bytes. This is needed for python3.2 compatibility
+        # because b64decode function only supports bytes type in input.
+        if isinstance(auth_string, six.string_types):
+            auth_string = auth_string.encode(encoding)
+
         try:
             b64_decoded = base64.b64decode(auth_string)
         except (TypeError, binascii.Error):
@@ -272,7 +278,7 @@ class OAuth2Validator(RequestValidator):
         return set(scopes).issubset(set(oauth2_settings._SCOPES))
 
     def get_default_scopes(self, client_id, request, *args, **kwargs):
-        return oauth2_settings._SCOPES
+        return oauth2_settings._DEFAULT_SCOPES
 
     def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs):
         return request.client.redirect_uri_allowed(redirect_uri)
diff --git a/oauth2_provider/settings.py b/oauth2_provider/settings.py
index db57686..7fb78e2 100644
--- a/oauth2_provider/settings.py
+++ b/oauth2_provider/settings.py
@@ -20,38 +20,43 @@ from __future__ import unicode_literals
 import six
 
 from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
 try:
     # Available in Python 2.7+
     import importlib
 except ImportError:
     from django.utils import importlib
 
-
 USER_SETTINGS = getattr(settings, 'OAUTH2_PROVIDER', None)
 
 DEFAULTS = {
     'CLIENT_ID_GENERATOR_CLASS': 'oauth2_provider.generators.ClientIdGenerator',
     'CLIENT_SECRET_GENERATOR_CLASS': 'oauth2_provider.generators.ClientSecretGenerator',
     'CLIENT_SECRET_GENERATOR_LENGTH': 128,
+    'OAUTH2_SERVER_CLASS': 'oauthlib.oauth2.Server',
     'OAUTH2_VALIDATOR_CLASS': 'oauth2_provider.oauth2_validators.OAuth2Validator',
     'OAUTH2_BACKEND_CLASS': 'oauth2_provider.oauth2_backends.OAuthLibCore',
     'SCOPES': {"read": "Reading scope", "write": "Writing scope"},
+    'DEFAULT_SCOPES': ['__all__'],
     'READ_SCOPE': 'read',
     'WRITE_SCOPE': 'write',
     'AUTHORIZATION_CODE_EXPIRE_SECONDS': 60,
     'ACCESS_TOKEN_EXPIRE_SECONDS': 36000,
+    'REFRESH_TOKEN_EXPIRE_SECONDS': None,
     'APPLICATION_MODEL': getattr(settings, 'OAUTH2_PROVIDER_APPLICATION_MODEL', 'oauth2_provider.Application'),
     'REQUEST_APPROVAL_PROMPT': 'force',
     'ALLOWED_REDIRECT_URI_SCHEMES': ['http', 'https'],
 
     # Special settings that will be evaluated at runtime
     '_SCOPES': [],
+    '_DEFAULT_SCOPES': [],
 }
 
 # List of settings that cannot be empty
 MANDATORY = (
     'CLIENT_ID_GENERATOR_CLASS',
     'CLIENT_SECRET_GENERATOR_CLASS',
+    'OAUTH2_SERVER_CLASS',
     'OAUTH2_VALIDATOR_CLASS',
     'OAUTH2_BACKEND_CLASS',
     'SCOPES',
@@ -62,6 +67,7 @@ MANDATORY = (
 IMPORT_STRINGS = (
     'CLIENT_ID_GENERATOR_CLASS',
     'CLIENT_SECRET_GENERATOR_CLASS',
+    'OAUTH2_SERVER_CLASS',
     'OAUTH2_VALIDATOR_CLASS',
     'OAUTH2_BACKEND_CLASS',
 )
@@ -125,6 +131,18 @@ class OAuth2ProviderSettings(object):
         # Overriding special settings
         if attr == '_SCOPES':
             val = list(six.iterkeys(self.SCOPES))
+        if attr == '_DEFAULT_SCOPES':
+            if '__all__' in self.DEFAULT_SCOPES:
+                # If DEFAULT_SCOPES is set to ['__all__'] the whole set of scopes is returned
+                val = list(self._SCOPES)
+            else:
+                # Otherwise we return a subset (that can be void) of SCOPES
+                val = []
+                for scope in self.DEFAULT_SCOPES:
+                    if scope in self._SCOPES:
+                        val.append(scope)
+                    else:
+                        raise ImproperlyConfigured("Defined DEFAULT_SCOPES not present in SCOPES")
 
         self.validate_setting(attr, val)
 
diff --git a/oauth2_provider/templates/oauth2_provider/application_confirm_delete.html b/oauth2_provider/templates/oauth2_provider/application_confirm_delete.html
index 1651f51..b1d944f 100644
--- a/oauth2_provider/templates/oauth2_provider/application_confirm_delete.html
+++ b/oauth2_provider/templates/oauth2_provider/application_confirm_delete.html
@@ -1,7 +1,7 @@
 {% extends "oauth2_provider/base.html" %}
 
 {% load i18n %}
-{% load url from future %}
+{% load url from compat %}
 {% block content %}
     <div class="block-center">
         <h3 class="block-center-heading">{% trans "Are you sure to delete the application" %} {{ application.name }}?</h3>
diff --git a/oauth2_provider/templates/oauth2_provider/application_detail.html b/oauth2_provider/templates/oauth2_provider/application_detail.html
index 36eb583..833f9a5 100644
--- a/oauth2_provider/templates/oauth2_provider/application_detail.html
+++ b/oauth2_provider/templates/oauth2_provider/application_detail.html
@@ -1,7 +1,7 @@
 {% extends "oauth2_provider/base.html" %}
 
 {% load i18n %}
-{% load url from future %}
+{% load url from compat %}
 {% block content %}
     <div class="block-center">
         <h3 class="block-center-heading">{{ application.name }}</h3>
diff --git a/oauth2_provider/templates/oauth2_provider/application_form.html b/oauth2_provider/templates/oauth2_provider/application_form.html
index baa81a1..5c08ff0 100644
--- a/oauth2_provider/templates/oauth2_provider/application_form.html
+++ b/oauth2_provider/templates/oauth2_provider/application_form.html
@@ -1,7 +1,7 @@
 {% extends "oauth2_provider/base.html" %}
 
 {% load i18n %}
-{% load url from future %}
+{% load url from compat %}
 {% block content %}
     <div class="block-center">
         <form class="form-horizontal" method="post" action="{% block app-form-action-url %}{% url 'oauth2_provider:update' application.id %}{% endblock app-form-action-url %}">
diff --git a/oauth2_provider/templates/oauth2_provider/application_list.html b/oauth2_provider/templates/oauth2_provider/application_list.html
index c500493..cb7c7c4 100644
--- a/oauth2_provider/templates/oauth2_provider/application_list.html
+++ b/oauth2_provider/templates/oauth2_provider/application_list.html
@@ -1,7 +1,7 @@
 {% extends "oauth2_provider/base.html" %}
 
 {% load i18n %}
-{% load url from future %}
+{% load url from compat %}
 {% block content %}
     <div class="block-center">
         <h3 class="block-center-heading">{% trans "Your applications" %}</h3>
diff --git a/oauth2_provider/templates/oauth2_provider/application_registration_form.html b/oauth2_provider/templates/oauth2_provider/application_registration_form.html
index 077e231..69bebb2 100644
--- a/oauth2_provider/templates/oauth2_provider/application_registration_form.html
+++ b/oauth2_provider/templates/oauth2_provider/application_registration_form.html
@@ -1,7 +1,7 @@
 {% extends "oauth2_provider/application_form.html" %}
 
 {% load i18n %}
-{% load url from future %}
+{% load url from compat %}
 
 {% block app-form-title %}{% trans "Register a new application" %}{% endblock app-form-title %}
 
diff --git a/oauth2_provider/templates/oauth2_provider/authorized-token-delete.html b/oauth2_provider/templates/oauth2_provider/authorized-token-delete.html
new file mode 100644
index 0000000..e08233a
--- /dev/null
+++ b/oauth2_provider/templates/oauth2_provider/authorized-token-delete.html
@@ -0,0 +1,9 @@
+{% extends "oauth2_provider/base.html" %}
+
+{% load i18n %}
+{% block content %}
+    <form action="" method="post">{% csrf_token %}
+        <p>{% trans "Are you sure you want to delete this token?" %}</p>
+        <input type="submit" value="{% trans "Delete" %}" />
+    </form>
+{% endblock %}
diff --git a/oauth2_provider/templates/oauth2_provider/authorized-tokens.html b/oauth2_provider/templates/oauth2_provider/authorized-tokens.html
new file mode 100644
index 0000000..f25069e
--- /dev/null
+++ b/oauth2_provider/templates/oauth2_provider/authorized-tokens.html
@@ -0,0 +1,24 @@
+{% extends "oauth2_provider/base.html" %}
+
+{% load i18n %}
+{% load url from compat %}
+{% block content %}
+    <div class="block-center">
+    <h1>{% trans "Tokens" %}</h1>
+        <ul>
+        {% for authorized_token in authorized_tokens %}
+            <li>
+                {{ authorized_token.application }}
+                (<a href="{% url 'oauth2_provider:authorized-token-delete' authorized_token.pk %}">revoke</a>)
+            </li>
+            <ul>
+            {% for scope_name, scope_description in authorized_token.scopes.items %}
+                <li>{{ scope_name }}: {{ scope_description }}</li>
+            {% endfor %}
+            </ul>
+        {% empty %}
+            <li>{% trans "There are no authorized tokens yet." %}</li>
+        {% endfor %}
+        </ul>
+    </div>
+{% endblock %}
diff --git a/oauth2_provider/templatetags/__init__.py b/oauth2_provider/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/oauth2_provider/templatetags/compat.py b/oauth2_provider/templatetags/compat.py
new file mode 100644
index 0000000..8fbc8b0
--- /dev/null
+++ b/oauth2_provider/templatetags/compat.py
@@ -0,0 +1,10 @@
+from django import template
+
+from ..compat import url as url_compat
+
+register = template.Library()
+
+
+ at register.tag
+def url(parser, token):
+    return url_compat(parser, token)
diff --git a/oauth2_provider/tests/settings.py b/oauth2_provider/tests/settings.py
index 47ffae0..e144e80 100644
--- a/oauth2_provider/tests/settings.py
... 1037 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-oauth-toolkit.git



More information about the Python-modules-commits mailing list