22
33from django .conf .urls import include , url
44from django .contrib .auth import get_user_model
5+ from django .core .exceptions import ImproperlyConfigured
56from django .http import HttpResponse
67from django .test import TestCase
78from django .test .utils import override_settings
89from django .utils import timezone
910from rest_framework import permissions
11+ from rest_framework .authentication import BaseAuthentication
1012from rest_framework .test import APIRequestFactory , force_authenticate
1113from rest_framework .views import APIView
1214
1315from oauth2_provider .contrib .rest_framework import (
1416 IsAuthenticatedOrTokenHasScope , OAuth2Authentication ,
15- TokenHasReadWriteScope , TokenHasResourceScope , TokenHasScope
17+ TokenHasMethodScopeAlternative , TokenHasReadWriteScope ,
18+ TokenHasResourceScope , TokenHasScope
1619)
1720from oauth2_provider .models import get_access_token_model , get_application_model
1821from oauth2_provider .settings import oauth2_settings
@@ -38,14 +41,17 @@ def get(self, request):
3841 def post (self , request ):
3942 return HttpResponse ({"a" : 1 , "b" : 2 , "c" : 3 })
4043
44+ def put (self , request ):
45+ return HttpResponse ({"a" : 1 , "b" : 2 , "c" : 3 })
46+
4147
4248class OAuth2View (MockView ):
4349 authentication_classes = [OAuth2Authentication ]
4450
4551
4652class ScopedView (OAuth2View ):
4753 permission_classes = [permissions .IsAuthenticated , TokenHasScope ]
48- required_scopes = ["scope1" ]
54+ required_scopes = ["scope1" , "another" ]
4955
5056
5157class AuthenticatedOrScopedView (OAuth2View ):
@@ -62,13 +68,48 @@ class ResourceScopedView(OAuth2View):
6268 required_scopes = ["resource1" ]
6369
6470
71+ class MethodScopeAltView (OAuth2View ):
72+ permission_classes = [TokenHasMethodScopeAlternative ]
73+ required_alternate_scopes = {
74+ "GET" : [["read" ]],
75+ "POST" : [["create" ]],
76+ "PUT" : [["update" , "put" ], ["update" , "edit" ]],
77+ "DELETE" : [["delete" ], ["deleter" , "write" ]],
78+ }
79+
80+
81+ class MethodScopeAltViewBad (OAuth2View ):
82+ permission_classes = [TokenHasMethodScopeAlternative ]
83+
84+
85+ class MissingAuthentication (BaseAuthentication ):
86+ def authenticate (self , request ):
87+ return ("junk" , "junk" ,)
88+
89+
90+ class BrokenOAuth2View (MockView ):
91+ authentication_classes = [MissingAuthentication ]
92+
93+
94+ class TokenHasScopeViewWrongAuth (BrokenOAuth2View ):
95+ permission_classes = [TokenHasScope ]
96+
97+
98+ class MethodScopeAltViewWrongAuth (BrokenOAuth2View ):
99+ permission_classes = [TokenHasMethodScopeAlternative ]
100+
101+
65102urlpatterns = [
66103 url (r"^oauth2/" , include ("oauth2_provider.urls" )),
67104 url (r"^oauth2-test/$" , OAuth2View .as_view ()),
68105 url (r"^oauth2-scoped-test/$" , ScopedView .as_view ()),
106+ url (r"^oauth2-scoped-missing-auth/$" , TokenHasScopeViewWrongAuth .as_view ()),
69107 url (r"^oauth2-read-write-test/$" , ReadWriteScopedView .as_view ()),
70108 url (r"^oauth2-resource-scoped-test/$" , ResourceScopedView .as_view ()),
71109 url (r"^oauth2-authenticated-or-scoped-test/$" , AuthenticatedOrScopedView .as_view ()),
110+ url (r"^oauth2-method-scope-test/.*$" , MethodScopeAltView .as_view ()),
111+ url (r"^oauth2-method-scope-fail/$" , MethodScopeAltViewBad .as_view ()),
112+ url (r"^oauth2-method-scope-missing-auth/$" , MethodScopeAltViewWrongAuth .as_view ()),
72113]
73114
74115
@@ -142,13 +183,19 @@ def test_authentication_or_scope_denied(self):
142183 self .assertEqual (response .status_code , 403 )
143184
144185 def test_scoped_permission_allow (self ):
145- self .access_token .scope = "scope1"
186+ self .access_token .scope = "scope1 another "
146187 self .access_token .save ()
147188
148189 auth = self ._create_authorization_header (self .access_token .token )
149190 response = self .client .get ("/oauth2-scoped-test/" , HTTP_AUTHORIZATION = auth )
150191 self .assertEqual (response .status_code , 200 )
151192
193+ def test_scope_missing_scope_attr (self ):
194+ auth = self ._create_authorization_header ("fake-token" )
195+ with self .assertRaises (AssertionError ) as e :
196+ self .client .get ("/oauth2-scoped-missing-auth/" , HTTP_AUTHORIZATION = auth )
197+ self .assertTrue ("`oauth2_provider.rest_framework.OAuth2Authentication`" in str (e .exception ))
198+
152199 def test_authenticated_or_scoped_permission_allow (self ):
153200 self .access_token .scope = "scope1"
154201 self .access_token .save ()
@@ -255,7 +302,7 @@ def test_required_scope_in_response(self):
255302 auth = self ._create_authorization_header (self .access_token .token )
256303 response = self .client .get ("/oauth2-scoped-test/" , HTTP_AUTHORIZATION = auth )
257304 self .assertEqual (response .status_code , 403 )
258- self .assertEqual (response .data ["required_scopes" ], ["scope1" ])
305+ self .assertEqual (response .data ["required_scopes" ], ["scope1" , "another" ])
259306
260307 def test_required_scope_not_in_response_by_default (self ):
261308 self .access_token .scope = "scope2"
@@ -265,3 +312,90 @@ def test_required_scope_not_in_response_by_default(self):
265312 response = self .client .get ("/oauth2-scoped-test/" , HTTP_AUTHORIZATION = auth )
266313 self .assertEqual (response .status_code , 403 )
267314 self .assertNotIn ("required_scopes" , response .data )
315+
316+ def test_method_scope_alt_permission_get_allow (self ):
317+ self .access_token .scope = "read"
318+ self .access_token .save ()
319+
320+ auth = self ._create_authorization_header (self .access_token .token )
321+ response = self .client .get ("/oauth2-method-scope-test/" , HTTP_AUTHORIZATION = auth )
322+ self .assertEqual (response .status_code , 200 )
323+
324+ def test_method_scope_alt_permission_post_allow (self ):
325+ self .access_token .scope = "create"
326+ self .access_token .save ()
327+
328+ auth = self ._create_authorization_header (self .access_token .token )
329+ response = self .client .post ("/oauth2-method-scope-test/" , HTTP_AUTHORIZATION = auth )
330+ self .assertEqual (response .status_code , 200 )
331+
332+ def test_method_scope_alt_permission_put_allow (self ):
333+ self .access_token .scope = "edit update"
334+ self .access_token .save ()
335+
336+ auth = self ._create_authorization_header (self .access_token .token )
337+ response = self .client .put ("/oauth2-method-scope-test/123" , HTTP_AUTHORIZATION = auth )
338+ self .assertEqual (response .status_code , 200 )
339+
340+ def test_method_scope_alt_permission_put_fail (self ):
341+ self .access_token .scope = "edit"
342+ self .access_token .save ()
343+
344+ auth = self ._create_authorization_header (self .access_token .token )
345+ response = self .client .put ("/oauth2-method-scope-test/123" , HTTP_AUTHORIZATION = auth )
346+ self .assertEqual (response .status_code , 403 )
347+
348+ def test_method_scope_alt_permission_get_deny (self ):
349+ self .access_token .scope = "write"
350+ self .access_token .save ()
351+
352+ auth = self ._create_authorization_header (self .access_token .token )
353+ response = self .client .get ("/oauth2-method-scope-test/" , HTTP_AUTHORIZATION = auth )
354+ self .assertEqual (response .status_code , 403 )
355+
356+ def test_method_scope_alt_permission_post_deny (self ):
357+ self .access_token .scope = "read"
358+ self .access_token .save ()
359+
360+ auth = self ._create_authorization_header (self .access_token .token )
361+ response = self .client .post ("/oauth2-method-scope-test/" , HTTP_AUTHORIZATION = auth )
362+ self .assertEqual (response .status_code , 403 )
363+
364+ def test_method_scope_alt_no_token (self ):
365+ self .access_token .scope = ""
366+ self .access_token .save ()
367+
368+ auth = self ._create_authorization_header (self .access_token .token )
369+ self .access_token = None
370+ response = self .client .post ("/oauth2-method-scope-test/" , HTTP_AUTHORIZATION = auth )
371+ self .assertEqual (response .status_code , 403 )
372+
373+ def test_method_scope_alt_missing_attr (self ):
374+ self .access_token .scope = "read"
375+ self .access_token .save ()
376+
377+ auth = self ._create_authorization_header (self .access_token .token )
378+ with self .assertRaises (ImproperlyConfigured ):
379+ self .client .post ("/oauth2-method-scope-fail/" , HTTP_AUTHORIZATION = auth )
380+
381+ def test_method_scope_alt_missing_patch_method (self ):
382+ self .access_token .scope = "update"
383+ self .access_token .save ()
384+
385+ auth = self ._create_authorization_header (self .access_token .token )
386+ response = self .client .patch ("/oauth2-method-scope-test/" , HTTP_AUTHORIZATION = auth )
387+ self .assertEqual (response .status_code , 403 )
388+
389+ def test_method_scope_alt_empty_scope (self ):
390+ self .access_token .scope = ""
391+ self .access_token .save ()
392+
393+ auth = self ._create_authorization_header (self .access_token .token )
394+ response = self .client .patch ("/oauth2-method-scope-test/" , HTTP_AUTHORIZATION = auth )
395+ self .assertEqual (response .status_code , 403 )
396+
397+ def test_method_scope_alt_missing_scope_attr (self ):
398+ auth = self ._create_authorization_header ("fake-token" )
399+ with self .assertRaises (AssertionError ) as e :
400+ self .client .get ("/oauth2-method-scope-missing-auth/" , HTTP_AUTHORIZATION = auth )
401+ self .assertTrue ("`oauth2_provider.rest_framework.OAuth2Authentication`" in str (e .exception ))
0 commit comments