Skip to content

Commit 8ee988d

Browse files
author
Massimiliano
committed
fixed bugs and better tests
1 parent 2fb85fa commit 8ee988d

File tree

2 files changed

+76
-51
lines changed

2 files changed

+76
-51
lines changed

oauth2_provider/decorators.py

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,55 +9,60 @@
99
from .settings import oauth2_settings
1010

1111

12-
def protected_resource(view_func, scopes=None, validator_cls=OAuth2Validator, server_cls=Server):
12+
def protected_resource(scopes=None, validator_cls=OAuth2Validator, server_cls=Server):
1313
"""
14-
Decorator to protect views by providing OAuth2 authentication out of the box, optionally with scope handling.
14+
Decorator to protect views by providing OAuth2 authentication out of the box, optionally with
15+
scope handling.
1516
"""
16-
if scopes is None:
17-
scopes = []
18-
19-
@wraps(view_func)
20-
def _validate(request, *args, **kwargs):
21-
validator = validator_cls()
22-
core = OAuthLibCore(server_cls(validator))
23-
valid, oauthlib_req = core.verify_request(request, scopes=scopes)
24-
if valid:
25-
return view_func(request, *args, **kwargs)
26-
return HttpResponseForbidden()
27-
return _validate
28-
29-
30-
def rw_protected_resource(view_func, scopes=None, validator_cls=OAuth2Validator, server_cls=Server):
17+
_scopes = scopes or []
18+
19+
def decorator(view_func):
20+
@wraps(view_func)
21+
def _validate(request, *args, **kwargs):
22+
validator = validator_cls()
23+
core = OAuthLibCore(server_cls(validator))
24+
valid, oauthlib_req = core.verify_request(request, scopes=_scopes)
25+
if valid:
26+
return view_func(request, *args, **kwargs)
27+
return HttpResponseForbidden()
28+
return _validate
29+
return decorator
30+
31+
32+
def rw_protected_resource(scopes=None, validator_cls=OAuth2Validator, server_cls=Server):
3133
"""
32-
Decorator to protect views by providing OAuth2 authentication and read/write scopes out of the box.
34+
Decorator to protect views by providing OAuth2 authentication and read/write scopes out of the
35+
box.
3336
GET, HEAD, OPTIONS http methods require "read" scope. Otherwise "write" scope is required.
3437
"""
35-
if scopes is None:
36-
scopes = []
37-
38-
@wraps(view_func)
39-
def _validate(request, *args, **kwargs):
40-
# Check if provided scopes are acceptable
41-
provided_scopes = oauth2_settings._SCOPES
42-
read_write_scopes = [oauth2_settings.READ_SCOPE, oauth2_settings.WRITE_SCOPE]
43-
44-
if not set(read_write_scopes).issubset(set(provided_scopes)):
45-
raise ImproperlyConfigured(
46-
"rw_protected_resource decorator requires following scopes {0}"
47-
" to be in OAUTH2_PROVIDER['SCOPES'] list in settings".format(read_write_scopes)
48-
)
49-
50-
# Check if method is safe
51-
if request.method.upper() in ['GET', 'HEAD', 'OPTIONS']:
52-
scopes.append(oauth2_settings.READ_SCOPE)
53-
else:
54-
scopes.append(oauth2_settings.WRITE_SCOPE)
55-
56-
# proceed with validation
57-
validator = validator_cls()
58-
core = OAuthLibCore(server_cls(validator))
59-
valid, oauthlib_req = core.verify_request(request, scopes=scopes)
60-
if valid:
61-
return view_func(request, *args, **kwargs)
62-
return HttpResponseForbidden()
63-
return _validate
38+
_scopes = scopes or []
39+
40+
def decorator(view_func):
41+
@wraps(view_func)
42+
def _validate(request, *args, **kwargs):
43+
# Check if provided scopes are acceptable
44+
provided_scopes = oauth2_settings._SCOPES
45+
read_write_scopes = [oauth2_settings.READ_SCOPE, oauth2_settings.WRITE_SCOPE]
46+
47+
if not set(read_write_scopes).issubset(set(provided_scopes)):
48+
raise ImproperlyConfigured(
49+
"rw_protected_resource decorator requires following scopes {0}"
50+
" to be in OAUTH2_PROVIDER['SCOPES'] list in settings".format(
51+
read_write_scopes)
52+
)
53+
54+
# Check if method is safe
55+
if request.method.upper() in ['GET', 'HEAD', 'OPTIONS']:
56+
_scopes.append(oauth2_settings.READ_SCOPE)
57+
else:
58+
_scopes.append(oauth2_settings.WRITE_SCOPE)
59+
60+
# proceed with validation
61+
validator = validator_cls()
62+
core = OAuthLibCore(server_cls(validator))
63+
valid, oauthlib_req = core.verify_request(request, scopes=_scopes)
64+
if valid:
65+
return view_func(request, *args, **kwargs)
66+
return HttpResponseForbidden()
67+
return _validate
68+
return decorator

oauth2_provider/tests/test_decorators.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,18 @@ def setUp(self):
3636
application=self.application
3737
)
3838

39+
self.another_access_token = AccessToken.objects.create(
40+
user=self.user,
41+
scope='can_touch_this',
42+
expires=timezone.now() + timedelta(seconds=300),
43+
token='secret-access-token-key2',
44+
application=self.application
45+
)
46+
3947
oauth2_settings._SCOPES = ['read', 'write']
4048

4149
def test_access_denied(self):
42-
@protected_resource
50+
@protected_resource()
4351
def view(request, *args, **kwargs):
4452
return 'protected contents'
4553

@@ -48,33 +56,45 @@ def view(request, *args, **kwargs):
4856
self.assertEqual(response.status_code, 403)
4957

5058
def test_access_allowed(self):
51-
@protected_resource
59+
@protected_resource()
5260
def view(request, *args, **kwargs):
5361
return 'protected contents'
5462

63+
@protected_resource(scopes=['can_touch_this'])
64+
def scoped_view(request, *args, **kwargs):
65+
return 'moar protected contents'
66+
5567
auth_headers = {
5668
'HTTP_AUTHORIZATION': 'Bearer ' + self.access_token.token,
5769
}
5870
request = self.request_factory.get("/fake-resource", **auth_headers)
5971
response = view(request)
6072
self.assertEqual(response, "protected contents")
6173

74+
# now with scopes
75+
auth_headers = {
76+
'HTTP_AUTHORIZATION': 'Bearer ' + self.another_access_token.token,
77+
}
78+
request = self.request_factory.get("/fake-resource", **auth_headers)
79+
response = scoped_view(request)
80+
self.assertEqual(response, "moar protected contents")
81+
6282
def test_rw_protected(self):
6383
self.access_token.scope = 'read'
6484
self.access_token.save()
6585
auth_headers = {
6686
'HTTP_AUTHORIZATION': 'Bearer ' + self.access_token.token,
6787
}
6888

69-
@rw_protected_resource
89+
@rw_protected_resource()
7090
def scoped_view(request, *args, **kwargs):
7191
return 'other protected contents'
7292

7393
request = self.request_factory.post("/fake-resource", **auth_headers)
7494
response = scoped_view(request)
7595
self.assertEqual(response.status_code, 403)
7696

77-
@rw_protected_resource
97+
@rw_protected_resource()
7898
def scoped_view(request, *args, **kwargs):
7999
return 'other protected contents'
80100

0 commit comments

Comments
 (0)