Skip to content

Commit 7071c0e

Browse files
synasiusjleclanche
authored andcommitted
Add support for scopes backends to allow easier customization of scopes
1 parent 846b6b1 commit 7071c0e

File tree

12 files changed

+94
-14
lines changed

12 files changed

+94
-14
lines changed

oauth2_provider/decorators.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from .oauth2_validators import OAuth2Validator
88
from .oauth2_backends import OAuthLibCore
9+
from .scopes import get_scopes_backend
910
from .settings import oauth2_settings
1011

1112

@@ -55,7 +56,7 @@ def decorator(view_func):
5556
@wraps(view_func)
5657
def _validate(request, *args, **kwargs):
5758
# Check if provided scopes are acceptable
58-
provided_scopes = oauth2_settings._SCOPES
59+
provided_scopes = get_scopes_backend().get_all_scopes()
5960
read_write_scopes = [oauth2_settings.READ_SCOPE, oauth2_settings.WRITE_SCOPE]
6061

6162
if not set(read_write_scopes).issubset(set(provided_scopes)):

oauth2_provider/models.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from django.utils.encoding import python_2_unicode_compatible
1212
from django.core.exceptions import ImproperlyConfigured
1313

14+
from .scopes import get_scopes_backend
1415
from .settings import oauth2_settings
1516
from .compat import parse_qsl, reverse, urlparse
1617
from .generators import generate_client_secret, generate_client_id
@@ -239,7 +240,9 @@ def scopes(self):
239240
"""
240241
Returns a dictionary of allowed scope names (as keys) with their descriptions (as values)
241242
"""
242-
return {name: desc for name, desc in oauth2_settings.SCOPES.items() if name in self.scope.split()}
243+
all_scopes = get_scopes_backend().get_all_scopes()
244+
token_scopes = self.scope.split()
245+
return {name: desc for name, desc in all_scopes.items() if name in token_scopes}
243246

244247
def __str__(self):
245248
return self.token

oauth2_provider/oauth2_validators.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from .compat import unquote_plus
1616
from .exceptions import FatalClientError
1717
from .models import Grant, AccessToken, RefreshToken, get_application_model, AbstractApplication
18+
from .scopes import get_scopes_backend
1819
from .settings import oauth2_settings
1920

2021

@@ -279,10 +280,12 @@ def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):
279280
"""
280281
Ensure required scopes are permitted (as specified in the settings file)
281282
"""
282-
return set(scopes).issubset(set(oauth2_settings._SCOPES))
283+
available_scopes = get_scopes_backend().get_available_scopes(application=client, request=request)
284+
return set(scopes).issubset(set(available_scopes))
283285

284286
def get_default_scopes(self, client_id, request, *args, **kwargs):
285-
return oauth2_settings._DEFAULT_SCOPES
287+
default_scopes = get_scopes_backend().get_default_scopes(application=request.client, request=request)
288+
return default_scopes
286289

287290
def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs):
288291
return request.client.redirect_uri_allowed(redirect_uri)

oauth2_provider/scopes.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from __future__ import absolute_import
2+
from __future__ import unicode_literals
3+
4+
from .settings import oauth2_settings
5+
6+
7+
class BaseScopes(object):
8+
def get_all_scopes(self):
9+
"""
10+
Return a dict-like object with all the scopes available in the
11+
system. The key should be the scope name and the value should be
12+
the description.
13+
14+
ex: {"read": "A read scope", "write": "A write scope"}
15+
"""
16+
raise NotImplementedError("")
17+
18+
def get_available_scopes(self, application=None, request=None, *args, **kwargs):
19+
"""
20+
Return a list of scopes available for the current application/request.
21+
22+
TODO: add info on where and why this method is called.
23+
24+
ex: ["read", "write"]
25+
"""
26+
raise NotImplementedError("")
27+
28+
def get_default_scopes(self, application=None, request=None, *args, **kwargs):
29+
"""
30+
Return a list of the default scopes for the current application/request.
31+
This MUST be a subset of the scopes returned by `get_available_scopes`.
32+
33+
TODO: add info on where and why this method is called.
34+
35+
ex: ["read"]
36+
"""
37+
raise NotImplementedError("")
38+
39+
40+
class SettingsScopes(BaseScopes):
41+
def get_all_scopes(self):
42+
return oauth2_settings.SCOPES
43+
44+
def get_available_scopes(self, application=None, request=None, *args, **kwargs):
45+
return oauth2_settings._SCOPES
46+
47+
def get_default_scopes(self, application=None, request=None, *args, **kwargs):
48+
return oauth2_settings._DEFAULT_SCOPES
49+
50+
51+
def get_scopes_backend():
52+
scopes_class = oauth2_settings.SCOPES_BACKEND_CLASS
53+
return scopes_class()

oauth2_provider/settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
'OAUTH2_BACKEND_CLASS': 'oauth2_provider.oauth2_backends.OAuthLibCore',
3636
'SCOPES': {"read": "Reading scope", "write": "Writing scope"},
3737
'DEFAULT_SCOPES': ['__all__'],
38+
'SCOPES_BACKEND_CLASS': 'oauth2_provider.scopes.SettingsScopes',
3839
'READ_SCOPE': 'read',
3940
'WRITE_SCOPE': 'write',
4041
'AUTHORIZATION_CODE_EXPIRE_SECONDS': 60,
@@ -68,6 +69,7 @@
6869
'OAUTH2_SERVER_CLASS',
6970
'OAUTH2_VALIDATOR_CLASS',
7071
'OAUTH2_BACKEND_CLASS',
72+
'SCOPES_BACKEND_CLASS',
7173
)
7274

7375

oauth2_provider/tests/settings.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,3 @@
126126
},
127127
}
128128
}
129-
130-
OAUTH2_PROVIDER = {
131-
'_SCOPES': ['example']
132-
}

oauth2_provider/tests/test_authorization_code.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,3 +1056,4 @@ def test_pre_auth_default_scopes(self):
10561056
self.assertEqual(form['state'].value(), "random_state_string")
10571057
self.assertEqual(form['scope'].value(), 'read')
10581058
self.assertEqual(form['client_id'].value(), self.application.client_id)
1059+
oauth2_settings._DEFAULT_SCOPES = ['read', 'write']

oauth2_provider/tests/test_rest_framework.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ def setUp(self):
9696
application=self.application
9797
)
9898

99+
def tearDown(self):
100+
oauth2_settings._SCOPES = ['read', 'write']
101+
99102
def _create_authorization_header(self, token):
100103
return "Bearer {0}".format(token)
101104

oauth2_provider/tests/test_scopes.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def setUp(self):
6060
oauth2_settings.WRITE_SCOPE = 'write'
6161

6262
def tearDown(self):
63+
oauth2_settings._SCOPES = ["read", "write"]
6364
self.application.delete()
6465
self.test_user.delete()
6566
self.dev_user.delete()
@@ -323,26 +324,26 @@ def get_access_token(self, scopes):
323324
return content['access_token']
324325

325326
def test_improperly_configured(self):
326-
oauth2_settings._SCOPES = ['scope1']
327+
oauth2_settings.SCOPES = {'scope1': 'Scope 1'}
327328

328329
request = self.factory.get("/fake")
329330
view = ReadWriteResourceView.as_view()
330331
self.assertRaises(ImproperlyConfigured, view, request)
331332

332-
oauth2_settings._SCOPES = ['read', 'write']
333+
oauth2_settings.SCOPES = {'read': 'Read Scope', 'write': 'Write Scope'}
333334
oauth2_settings.READ_SCOPE = 'ciccia'
334335

335336
view = ReadWriteResourceView.as_view()
336337
self.assertRaises(ImproperlyConfigured, view, request)
337338

338339
def test_properly_configured(self):
339-
oauth2_settings._SCOPES = ['scope1']
340+
oauth2_settings.SCOPES = {'scope1': 'Scope 1'}
340341

341342
request = self.factory.get("/fake")
342343
view = ReadWriteResourceView.as_view()
343344
self.assertRaises(ImproperlyConfigured, view, request)
344345

345-
oauth2_settings._SCOPES = ['read', 'write']
346+
oauth2_settings.SCOPES = {'read': 'Read Scope', 'write': 'Write Scope'}
346347
oauth2_settings.READ_SCOPE = 'ciccia'
347348

348349
view = ReadWriteResourceView.as_view()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from __future__ import absolute_import
2+
from __future__ import unicode_literals
3+
4+
from oauth2_provider.scopes import SettingsScopes
5+
6+
7+
def test_settings_scopes_get_available_scopes():
8+
scopes = SettingsScopes()
9+
assert scopes.get_available_scopes() == ["read", "write"]
10+
11+
12+
def test_settings_scopes_get_default_scopes():
13+
scopes = SettingsScopes()
14+
assert scopes.get_default_scopes() == ["read", "write"]

0 commit comments

Comments
 (0)