Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,32 @@ The import string of the class (model) representing your applications. Overwrite
this value if you wrote your own implementation (subclass of
``oauth2_provider.models.Application``).

APPLICATION_REGISTRATION_PERMISSIONS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Defines which permissions are required to be able to register an application.
By default every authenticated user is able to register applications.

A common setup for none public sites might be to only allow superusers and users
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/none/non/

with the ```oauth2_provider | application | Can add application``` permission to be allowed
to register applications.

This can be done with the following configuration::

OAUTH2_PROVIDER = {
...
'APPLICATION_REGISTRATION_PERMISSIONS': {
'all': ('oauth2_provider.add_application', ),
},
...
}

Following Django's auth system you can now either grant the ```oauth2_provider | application | Can add application```
the a specific user or group to allow access to the application registration page.

For more information about setting more complex permissions see
`MultiplePermissionsRequiredMixin <https://django-braces.readthedocs.org/en/latest/access.html#multiplepermissionsrequiredmixin>`_.


AUTHORIZATION_CODE_EXPIRE_SECONDS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The number of seconds an authorization code remains valid. Requesting an access
Expand Down
4 changes: 4 additions & 0 deletions oauth2_provider/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
'AUTHORIZATION_CODE_EXPIRE_SECONDS': 60,
'ACCESS_TOKEN_EXPIRE_SECONDS': 36000,
'APPLICATION_MODEL': getattr(settings, 'OAUTH2_PROVIDER_APPLICATION_MODEL', 'oauth2_provider.Application'),
'APPLICATION_REGISTRATION_PERMISSIONS': None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key is duplicated

'APPLICATION_REGISTRATION_PERMISSIONS': {
'all': ('oauth2_provider.add_application', ),
},
'REQUEST_APPROVAL_PROMPT': 'force',
'ALLOWED_REDIRECT_URI_SCHEMES': ['http', 'https'],

Expand Down
59 changes: 59 additions & 0 deletions oauth2_provider/tests/test_application_views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from __future__ import unicode_literals

from django.core.urlresolvers import reverse
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase

from ..settings import oauth2_settings
from ..models import get_application_model
from ..compat import get_user_model

Expand All @@ -15,6 +18,10 @@ def setUp(self):
self.foo_user = UserModel.objects.create_user("foo_user", "[email protected]", "123456")
self.bar_user = UserModel.objects.create_user("bar_user", "[email protected]", "123456")

application_content_type = ContentType.objects.get_for_model(Application)
add_application_permission = Permission.objects.get(content_type=application_content_type, codename='add_application')
self.foo_user.user_permissions.add(add_application_permission)

def tearDown(self):
self.foo_user.delete()
self.bar_user.delete()
Expand All @@ -40,6 +47,58 @@ def test_application_registration_user(self):
self.assertEqual(app.user.username, "foo_user")


class TestApplicationRegistrationViewPermissions(BaseTest):
def setUp(self):
super(TestApplicationRegistrationViewPermissions, self).setUp()
oauth2_settings.APPLICATION_REGISTRATION_PERMISSIONS = {
'all': ('oauth2_provider.add_application', ),
}

def tearDown(self):
super(TestApplicationRegistrationViewPermissions, self).tearDown()
oauth2_settings.APPLICATION_REGISTRATION_PERMISSIONS = None

def test_application_registration_user_with_permission(self):
self.client.login(username="foo_user", password="123456")

form_data = {
'name': 'Foo app',
'client_id': 'client_id',
'client_secret': 'client_secret',
'client_type': Application.CLIENT_CONFIDENTIAL,
'redirect_uris': 'http://example.com',
'authorization_grant_type': Application.GRANT_AUTHORIZATION_CODE
}

response = self.client.post(reverse('oauth2_provider:register'), form_data)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.has_header('Location'), True)
self.assertEqual(response['Location'].endswith("/o/applications/1/"), True)

app = Application.objects.get(name="Foo app")
self.assertEqual(app.user.username, "foo_user")

def test_application_registration_user_without_permission(self):
self.client.login(username="bar_user", password="123456")

form_data = {
'name': 'Bar app',
'client_id': 'client_id',
'client_secret': 'client_secret',
'client_type': Application.CLIENT_CONFIDENTIAL,
'redirect_uris': 'http://example.com',
'authorization_grant_type': Application.GRANT_AUTHORIZATION_CODE
}

response = self.client.post(reverse('oauth2_provider:register'), form_data)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.has_header('Location'), True)
self.assertEqual(response['Location'].endswith("/accounts/login/?next=/o/applications/register/"), True)

with self.assertRaises(Application.DoesNotExist):
app = Application.objects.get(name="Bar app")


class TestApplicationViews(BaseTest):
def _create_application(self, name, user):
app = Application.objects.create(
Expand Down
11 changes: 9 additions & 2 deletions oauth2_provider/views/application.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from django.core.urlresolvers import reverse_lazy
from django.views.generic import CreateView, DetailView, DeleteView, ListView, UpdateView

from braces.views import LoginRequiredMixin
from braces.views import LoginRequiredMixin, MultiplePermissionsRequiredMixin

from ..forms import RegistrationForm
from ..models import get_application_model
from ..settings import oauth2_settings


class ApplicationOwnerIsUserMixin(LoginRequiredMixin):
Expand All @@ -17,13 +18,19 @@ def get_queryset(self):
return get_application_model().objects.filter(user=self.request.user)


class ApplicationRegistration(LoginRequiredMixin, CreateView):
class ApplicationRegistration(LoginRequiredMixin, MultiplePermissionsRequiredMixin, CreateView):
"""
View used to register a new Application for the request.user
"""
form_class = RegistrationForm
permissions = oauth2_settings.APPLICATION_REGISTRATION_PERMISSIONS
template_name = "oauth2_provider/application_registration_form.html"

def check_permissions(self, request):
if getattr(self, 'permissions', None) is None:
return True
return super(ApplicationRegistration, self).check_permissions(request)

def form_valid(self, form):
form.instance.user = self.request.user
return super(ApplicationRegistration, self).form_valid(form)
Expand Down