From 49845ca79c06e3f38407e0fbc5013c5c44fb5515 Mon Sep 17 00:00:00 2001 From: Kevin Brown Date: Wed, 26 Feb 2014 16:19:22 -0500 Subject: [PATCH 1/6] First pass at PEP8 --- tests/tests/views/test_token.py | 66 +++++++++++++++++---------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/tests/tests/views/test_token.py b/tests/tests/views/test_token.py index 7cff792..cb6e26c 100644 --- a/tests/tests/views/test_token.py +++ b/tests/tests/views/test_token.py @@ -7,76 +7,78 @@ class TestTokenErrors(TokenTestCase): - + def test_grant_type(self): - from doac.exceptions.unsupported_grant_type import GrantTypeNotProvided, GrantTypeNotValid - + from doac.exceptions.unsupported_grant_type import \ + GrantTypeNotProvided, GrantTypeNotValid + request = self.client.post(reverse("oauth2_token")) self.assertExceptionJson(request, GrantTypeNotProvided()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": ""}) self.assertExceptionJson(request, GrantTypeNotProvided()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "invalid"}) self.assertExceptionJson(request, GrantTypeNotValid()) - + def test_client_id(self): from doac.exceptions.invalid_request import ClientNotProvided from doac.exceptions.invalid_client import ClientDoesNotExist - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code"}) self.assertExceptionJson(request, ClientNotProvided()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": ""}) self.assertExceptionJson(request, ClientNotProvided()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": 1234}) self.assertExceptionJson(request, ClientDoesNotExist()) - + def test_client_secret(self): from doac.exceptions.invalid_client import ClientSecretNotValid from doac.exceptions.invalid_request import ClientSecretNotProvided - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id}) self.assertExceptionJson(request, ClientSecretNotProvided()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": ""}) self.assertExceptionJson(request, ClientSecretNotProvided()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": "notVerySecret"}) self.assertExceptionJson(request, ClientSecretNotValid()) - + def test_code(self): from doac.exceptions.invalid_request import AuthorizationCodeAlreadyUsed, AuthorizationCodeNotProvided, AuthorizationCodeNotValid - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": self.client_secret}) self.assertExceptionJson(request, AuthorizationCodeNotProvided()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "code": ""}) self.assertExceptionJson(request, AuthorizationCodeNotProvided()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "code": "invalid"}) self.assertExceptionJson(request, AuthorizationCodeNotValid()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "code": self.authorization_token.token}) request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "code": self.authorization_token.token}) self.assertExceptionJson(request, AuthorizationCodeAlreadyUsed()) - + def test_refresh_token(self): - from doac.exceptions.invalid_request import RefreshTokenNotProvided, RefreshTokenNotValid - + from doac.exceptions.invalid_request import \ + RefreshTokenNotProvided, RefreshTokenNotValid + request = self.client.post(reverse("oauth2_token"), {"grant_type": "refresh_token", "client_id": self.oauth_client.id, "client_secret": self.client_secret}) self.assertExceptionJson(request, RefreshTokenNotProvided()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "refresh_token", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "refresh_token": ""}) self.assertExceptionJson(request, RefreshTokenNotProvided()) - + request = self.client.post(reverse("oauth2_token"), {"grant_type": "refresh_token", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "refresh_token": "invalid"}) self.assertExceptionJson(request, RefreshTokenNotValid()) class TestTokenResponse(TokenTestCase): - + def test_authorization_token(self): data = { "grant_type": "authorization_code", @@ -84,36 +86,36 @@ def test_authorization_token(self): "client_secret": self.client_secret, "code": self.authorization_token.token, } - + request = self.client.post(reverse("oauth2_token"), data) - + response = { "refresh_token": self.authorization_token.refresh_token.token, "token_type": "bearer", "expires_in": 5183999, "access_token": self.authorization_token.refresh_token.access_tokens.all()[0].token, } - + self.assertEqual(request.content, json.dumps(response)) self.assertEqual(request.status_code, 200) - + def test_refresh_token(self): refresh_token = self.authorization_token.generate_refresh_token() - + data = { "grant_type": "refresh_token", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "refresh_token": refresh_token.token, } - + request = self.client.post(reverse("oauth2_token"), data) - + response = { "token_type": "bearer", "expires_in": 7199, "access_token": refresh_token.access_tokens.all()[0].token, } - + self.assertEqual(request.content, json.dumps(response)) self.assertEqual(request.status_code, 200) From 85fd0a43590b4576e9a95e1525e9bf925ea64b47 Mon Sep 17 00:00:00 2001 From: Kevin Brown Date: Wed, 26 Feb 2014 16:41:48 -0500 Subject: [PATCH 2/6] Token tests PEP8 fixes --- tests/tests/views/test_token.py | 91 +++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 20 deletions(-) diff --git a/tests/tests/views/test_token.py b/tests/tests/views/test_token.py index cb6e26c..22b0b19 100644 --- a/tests/tests/views/test_token.py +++ b/tests/tests/views/test_token.py @@ -12,68 +12,116 @@ def test_grant_type(self): from doac.exceptions.unsupported_grant_type import \ GrantTypeNotProvided, GrantTypeNotValid - request = self.client.post(reverse("oauth2_token")) + data = {} + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, GrantTypeNotProvided()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": ""}) + data["grant_type"] = "" + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, GrantTypeNotProvided()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "invalid"}) + data["grant_type"] = "invalid" + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, GrantTypeNotValid()) def test_client_id(self): from doac.exceptions.invalid_request import ClientNotProvided from doac.exceptions.invalid_client import ClientDoesNotExist - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code"}) + data = { + "grant_type": "authorization_code", + } + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, ClientNotProvided()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": ""}) + data["client_id"] = "" + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, ClientNotProvided()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": 1234}) + data["client_id"] = "1234" + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, ClientDoesNotExist()) def test_client_secret(self): from doac.exceptions.invalid_client import ClientSecretNotValid from doac.exceptions.invalid_request import ClientSecretNotProvided - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id}) + data = { + "grant_type": "authorization_code", + "client_id": self.oauth_client.id, + } + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, ClientSecretNotProvided()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": ""}) + data["client_secret"] = "" + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, ClientSecretNotProvided()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": "notVerySecret"}) + data["client_secret"] = "notVerySecret" + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, ClientSecretNotValid()) def test_code(self): - from doac.exceptions.invalid_request import AuthorizationCodeAlreadyUsed, AuthorizationCodeNotProvided, AuthorizationCodeNotValid + from doac.exceptions.invalid_request import \ + AuthorizationCodeAlreadyUsed, AuthorizationCodeNotProvided, \ + AuthorizationCodeNotValid + + data = { + "grant_type": "authorization_code", + "client_id": self.oauth_client.id, + "client_secret": self.client_secret, + } - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": self.client_secret}) + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, AuthorizationCodeNotProvided()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "code": ""}) + data["code"] = "" + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, AuthorizationCodeNotProvided()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "code": "invalid"}) + data["code"] = "invalid" + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, AuthorizationCodeNotValid()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "code": self.authorization_token.token}) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "authorization_code", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "code": self.authorization_token.token}) + data["code"] = self.authorization_token.token + + request = self.client.post(reverse("oauth2_token"), data) + request = self.client.post(reverse("oauth2_token"), data) + self.assertExceptionJson(request, AuthorizationCodeAlreadyUsed()) def test_refresh_token(self): from doac.exceptions.invalid_request import \ RefreshTokenNotProvided, RefreshTokenNotValid - request = self.client.post(reverse("oauth2_token"), {"grant_type": "refresh_token", "client_id": self.oauth_client.id, "client_secret": self.client_secret}) + data = { + "grant_type": "refresh_token", + "client_id": self.oauth_client.id, + "client_secret": self.client_secret, + } + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, RefreshTokenNotProvided()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "refresh_token", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "refresh_token": ""}) + data["refresh_token"] = "" + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, RefreshTokenNotProvided()) - request = self.client.post(reverse("oauth2_token"), {"grant_type": "refresh_token", "client_id": self.oauth_client.id, "client_secret": self.client_secret, "refresh_token": "invalid"}) + data["refresh_token"] = "invalid" + + request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, RefreshTokenNotValid()) @@ -89,11 +137,14 @@ def test_authorization_token(self): request = self.client.post(reverse("oauth2_token"), data) + refresh_token = self.authorization_token.refresh_token + access_token = refresh_token.access_tokens.all()[0] + response = { - "refresh_token": self.authorization_token.refresh_token.token, + "refresh_token": refresh_token.token, "token_type": "bearer", "expires_in": 5183999, - "access_token": self.authorization_token.refresh_token.access_tokens.all()[0].token, + "access_token": access_token.token, } self.assertEqual(request.content, json.dumps(response)) From d1f61a4f167b47d2ac197614470692b3ea94ee3f Mon Sep 17 00:00:00 2001 From: Kevin Brown Date: Wed, 26 Feb 2014 16:58:02 -0500 Subject: [PATCH 3/6] Added test for password grant --- tests/tests/views/test_token.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/tests/views/test_token.py b/tests/tests/views/test_token.py index 22b0b19..a6eceab 100644 --- a/tests/tests/views/test_token.py +++ b/tests/tests/views/test_token.py @@ -170,3 +170,32 @@ def test_refresh_token(self): self.assertEqual(request.content, json.dumps(response)) self.assertEqual(request.status_code, 200) + + def test_password(self): + from doac.models import RefreshToken + + data = { + "grant_type": "password", + "client_id": self.oauth_client.id, + "client_secret": self.client_secret, + "username": self.user.username, + "password": "test", + } + + request = self.client.post(reverse("oauth2_token"), data) + + self.assertEqual(RefreshToken.objects.count(), 1) + + refresh_token = RefreshToken.objects.get() + access_token = refresh_token.access_tokens.get() + + self.assertEqual(request.status_code, 200) + + response = { + "token_type": "bearer", + "expires_in": 7199, + "refresh_token": refresh_token.token, + "access_token": access_token.token, + } + + self.assertEqual(request.content, json.dumps(response)) From 9a325ea82d107a3e5287abbeef9daa320282281b Mon Sep 17 00:00:00 2001 From: Kevin Brown Date: Wed, 26 Feb 2014 17:03:11 -0500 Subject: [PATCH 4/6] PEP8 fixes in views --- doac/views.py | 84 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/doac/views.py b/doac/views.py index 9f106bc..ee4d6bf 100644 --- a/doac/views.py +++ b/doac/views.py @@ -15,18 +15,19 @@ class OAuthView(View): """ All views must subclass this class. - This provides common methods which are needed for validation and processing OAuth - requests and responses. + This provides common methods which are needed for validation and + processing OAuth requests and responses. """ def handle_exception(self, exception): """ - Handle a unspecified exception and return the correct method that should be used - for handling it. + Handle a unspecified exception and return the correct method that + should be used for handling it. If the exception has the `can_redirect` property set to False, it is - rendered to the browser. Otherwise, it will be redirected to the location - provided in the `RedirectUri` object that is associated with the request. + rendered to the browser. Otherwise, it will be redirected to the + location provided in the `RedirectUri` object that is associated with + the request. """ can_redirect = getattr(exception, "can_redirect", True) @@ -63,7 +64,8 @@ def render_exception(self, exception): def render_exception_js(self, exception): """ - Return a response with the body containing a JSON-formatter version of the exception. + Return a response with the body containing a JSON-formatter version of + the exception. """ from .http import JsonResponse @@ -76,12 +78,13 @@ def render_exception_js(self, exception): def verify_dictionary(self, dict, *args): """ - Based on a provided `dict`, validate all of the contents of that dictionary that are - provided. + Based on a provided `dict`, validate all of the contents of that + dictionary that are provided. - For each argument provided that isn't the dictionary, this will set the raw value of - that key as the instance variable of the same name. It will then call the verification - function named `verify_[argument]` to verify the data. + For each argument provided that isn't the dictionary, this will set the + raw value of that key as the instance variable of the same name. It + will then call the verification function named `verify_[argument]` to + verify the data. """ for arg in args: @@ -93,8 +96,8 @@ def verify_dictionary(self, dict, *args): def verify_client_id(self): """ - Verify a provided client id against the database and set the `Client` object that is - associated with it to `self.client`. + Verify a provided client id against the database and set the `Client` + object that is associated with it to `self.client`. TODO: Document all of the thrown exceptions. """ @@ -106,7 +109,8 @@ def verify_client_id(self): if self.client_id: try: self.client = Client.objects.for_id(self.client_id) - # Catching also ValueError for the case when client_id doesn't contain an integer. + # Catching also ValueError for the case when client_id doesn't + # contain an integer. except (Client.DoesNotExist, ValueError): raise ClientDoesNotExist() else: @@ -115,7 +119,8 @@ def verify_client_id(self): def verify_redirect_uri(self): from urlparse import urlparse from .models import RedirectUri - from .exceptions.invalid_request import RedirectUriDoesNotValidate, RedirectUriNotProvided + from .exceptions.invalid_request import RedirectUriDoesNotValidate, \ + RedirectUriNotProvided PARSE_MATCH_ATTRIBUTES = ("scheme", "hostname", "port", ) @@ -133,7 +138,8 @@ def verify_redirect_uri(self): raise RedirectUriDoesNotValidate() try: - self.redirect_uri = RedirectUri.objects.with_client(self.client).for_url(self.redirect_uri) + self.redirect_uri = RedirectUri.objects \ + .with_client(self.client).for_url(self.redirect_uri) except RedirectUri.DoesNotExist: raise RedirectUriDoesNotValidate() else: @@ -166,7 +172,8 @@ def authorization_accepted(self): from django.http import HttpResponseRedirect from .models import AuthorizationToken - self.authorization_token = AuthorizationToken(user=self.request.user, client=self.client) + self.authorization_token = AuthorizationToken(user=self.request.user, + client=self.client) self.authorization_token.save() self.authorization_token.scope = self.scopes @@ -177,11 +184,14 @@ def authorization_accepted(self): else: separator = "#" - self.access_token = self.authorization_token.generate_refresh_token().generate_access_token() + self.access_token = self.authorization_token \ + .generate_refresh_token() \ + .generate_access_token() query_string = self.generate_query_string() - return HttpResponseRedirect(self.redirect_uri.url + separator + query_string) + return HttpResponseRedirect(self.redirect_uri.url + separator + + query_string) def authorization_denied(self): from .exceptions.access_denied import AuthorizationDenied @@ -203,7 +213,8 @@ def generate_query_string(self): def verify_code(self): from .models import AuthorizationCode - from .exceptions.invalid_request import AuthorizationCodeNotValid, AuthorizationCodeNotProvided + from .exceptions.invalid_request import AuthorizationCodeNotValid, \ + AuthorizationCodeNotProvided if self.code: get_code = self.request.GET.get("code", None) @@ -212,7 +223,8 @@ def verify_code(self): raise AuthorizationCodeNotValid() try: - self.authorization_code = AuthorizationCode.objects.for_token(self.code) + self.authorization_code = AuthorizationCode.objects \ + .for_token(self.code) except AuthorizationCode.DoesNotExist: raise AuthorizationCodeNotValid() else: @@ -231,7 +243,8 @@ def get(self, request, *args, **kwargs): self.state = request.GET.get("state", "o2cs") try: - self.verify_dictionary(request.GET, "client_id", "redirect_uri", "scope", "response_type") + self.verify_dictionary(request.GET, "client_id", "redirect_uri", + "scope", "response_type") except Exception, e: return self.handle_exception(e) @@ -253,7 +266,9 @@ def get(self, request, *args, **kwargs): def generate_authorization_code(self): from .models import AuthorizationCode - code = AuthorizationCode(client=self.client, redirect_uri=self.redirect_uri, response_type=self.response_type) + code = AuthorizationCode(client=self.client, + redirect_uri=self.redirect_uri, + response_type=self.response_type) code.save() code.scope = self.scopes @@ -300,7 +315,8 @@ def dispatch(self, *args, **kwargs): def post(self, request, *args, **kwargs): try: - self.verify_dictionary(request.POST, "grant_type", "client_id", "client_secret") + self.verify_dictionary(request.POST, "grant_type", "client_id", + "client_secret") except Exception, e: return self.render_exception_js(e) @@ -310,7 +326,8 @@ def post(self, request, *args, **kwargs): except Exception, e: return self.render_exception_js(e) - self.refresh_token = self.authorization_token.generate_refresh_token() + self.refresh_token = self.authorization_token \ + .generate_refresh_token() if not self.refresh_token: self.authorization_token.revoke_tokens() @@ -367,12 +384,14 @@ def verify_client_secret(self): raise ClientSecretNotProvided() def verify_code(self): - from .exceptions.invalid_request import AuthorizationCodeAlreadyUsed, AuthorizationCodeNotProvided, AuthorizationCodeNotValid + from .exceptions.invalid_request import AuthorizationCodeAlreadyUsed, \ + AuthorizationCodeNotProvided, AuthorizationCodeNotValid from .models import AuthorizationToken if self.code: try: - self.authorization_token = AuthorizationToken.objects.with_client(self.client).for_token(self.code) + self.authorization_token = AuthorizationToken.objects \ + .with_client(self.client).for_token(self.code) if not self.authorization_token.is_active: self.authorization_token.revoke_tokens() @@ -384,7 +403,8 @@ def verify_code(self): raise AuthorizationCodeNotProvided() def verify_grant_type(self): - from .exceptions.unsupported_grant_type import GrantTypeNotProvided, GrantTypeNotValid + from .exceptions.unsupported_grant_type import GrantTypeNotProvided, \ + GrantTypeNotValid self.grant_type = self.request.POST.get("grant_type", None) @@ -395,12 +415,14 @@ def verify_grant_type(self): raise GrantTypeNotProvided() def verify_refresh_token(self): - from .exceptions.invalid_request import RefreshTokenNotProvided, RefreshTokenNotValid + from .exceptions.invalid_request import RefreshTokenNotProvided, \ + RefreshTokenNotValid from .models import RefreshToken if self.refresh_token: try: - self.refresh_token = RefreshToken.objects.with_client(self.client).for_token(self.refresh_token) + self.refresh_token = RefreshToken.objects \ + .with_client(self.client).for_token(self.refresh_token) except RefreshToken.DoesNotExist: raise RefreshTokenNotValid() else: From fce97dd4494e2d2c69914744ae9b450683567b7b Mon Sep 17 00:00:00 2001 From: Kevin Brown Date: Wed, 26 Feb 2014 17:45:06 -0500 Subject: [PATCH 5/6] Started working on password credential grants This adds basic support for password credential grants that meets the minimums set in place by the tests. --- doac/views.py | 48 ++++++++++++++++++++++++++++++++- tests/tests/views/test_token.py | 2 +- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/doac/views.py b/doac/views.py index ee4d6bf..43061a2 100644 --- a/doac/views.py +++ b/doac/views.py @@ -8,7 +8,7 @@ ALLOWED_RESPONSE_TYPES = ("code", "token", ) -ALLOWED_GRANT_TYPES = ("authorization_code", "refresh_token", ) +ALLOWED_GRANT_TYPES = ("authorization_code", "refresh_token", "password", ) class OAuthView(View): @@ -346,6 +346,15 @@ def post(self, request, *args, **kwargs): return self.render_refresh_token() + elif self.grant_type == "password": + try: + self.verify_dictionary(request.POST, "scope") + self.verify_user() + except Exception, e: + return self.render_exception_js(e) + + return self.render_password() + def render_authorization_token(self): from .compat import now from .http import JsonResponse @@ -373,6 +382,29 @@ def render_refresh_token(self): return JsonResponse(response) + def render_password(self): + from doac.compat import now + from doac.http import JsonResponse + from doac.models import AuthorizationToken + + self.authorization_token = AuthorizationToken(user=self.user, + client=self.client) + self.authorization_token.save() + + self.refresh_token = self.authorization_token.generate_refresh_token() + self.access_token = self.refresh_token.generate_access_token() + + remaining = self.access_token.expires_at - now() + + response = { + "access_token": self.access_token.token, + "token_type": "bearer", + "expires_in": int(total_seconds(remaining)), + "refresh_token": self.refresh_token.token, + } + + return JsonResponse(response) + def verify_client_secret(self): from .exceptions.invalid_client import ClientSecretNotValid from .exceptions.invalid_request import ClientSecretNotProvided @@ -427,3 +459,17 @@ def verify_refresh_token(self): raise RefreshTokenNotValid() else: raise RefreshTokenNotProvided() + + def verify_user(self): + from django.contrib.auth import authenticate + + username = self.request.POST.get("username", None) + password = self.request.POST.get("password", None) + + if not username or not password: + raise CouldNotAuthenticate() + + self.user = authenticate(username=username, password=password) + + if not self.user or not self.user.is_active: + raise CouldNotAuthenticate() diff --git a/tests/tests/views/test_token.py b/tests/tests/views/test_token.py index a6eceab..d84672f 100644 --- a/tests/tests/views/test_token.py +++ b/tests/tests/views/test_token.py @@ -192,10 +192,10 @@ def test_password(self): self.assertEqual(request.status_code, 200) response = { + "access_token": access_token.token, "token_type": "bearer", "expires_in": 7199, "refresh_token": refresh_token.token, - "access_token": access_token.token, } self.assertEqual(request.content, json.dumps(response)) From bcb9c1670525ed2c21d7114ee08afa9097a654b1 Mon Sep 17 00:00:00 2001 From: Kevin Brown Date: Fri, 9 May 2014 20:59:21 -0400 Subject: [PATCH 6/6] Added error tests This adds tests to check the errors for the password grant. At this point, the password grant implementation is complete and under full test coverage. The two errors for password authentication, dealing with client credentials that are not included and those which are not valid, have been added and the code has been adjusted to use them. --- doac/exceptions/invalid_request.py | 8 ++++++++ doac/views.py | 6 ++++-- tests/tests/views/test_token.py | 23 +++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/doac/exceptions/invalid_request.py b/doac/exceptions/invalid_request.py index 882a247..2a50293 100644 --- a/doac/exceptions/invalid_request.py +++ b/doac/exceptions/invalid_request.py @@ -13,6 +13,14 @@ class AuthorizationCodeNotValid(InvalidRequest): reason = "The authorization code was malformed or invalid." +class ClientCredentialsNotProvided(InvalidRequest): + reason = "The client credentials were not provided." + + +class ClientCredentialsNotValid(InvalidRequest): + reason = "The client credentials were malformed or invalid." + + class ClientNotProvided(InvalidRequest): reason = "The client was not provided." diff --git a/doac/views.py b/doac/views.py index 43061a2..3cb1a44 100644 --- a/doac/views.py +++ b/doac/views.py @@ -462,14 +462,16 @@ def verify_refresh_token(self): def verify_user(self): from django.contrib.auth import authenticate + from doac.exceptions.invalid_request import \ + ClientCredentialsNotProvided, ClientCredentialsNotValid username = self.request.POST.get("username", None) password = self.request.POST.get("password", None) if not username or not password: - raise CouldNotAuthenticate() + raise ClientCredentialsNotProvided() self.user = authenticate(username=username, password=password) if not self.user or not self.user.is_active: - raise CouldNotAuthenticate() + raise ClientCredentialsNotValid() diff --git a/tests/tests/views/test_token.py b/tests/tests/views/test_token.py index d84672f..54379d6 100644 --- a/tests/tests/views/test_token.py +++ b/tests/tests/views/test_token.py @@ -124,6 +124,29 @@ def test_refresh_token(self): request = self.client.post(reverse("oauth2_token"), data) self.assertExceptionJson(request, RefreshTokenNotValid()) + def test_password_authentication(self): + from doac.exceptions.invalid_request import \ + ClientCredentialsNotProvided, ClientCredentialsNotValid + + data = { + "grant_type": "password", + "client_id": self.oauth_client.id, + "client_secret": self.client_secret, + } + + request = self.client.post(reverse("oauth2_token"), data) + self.assertExceptionJson(request, ClientCredentialsNotProvided()) + + data["username"] = self.user.username + + request = self.client.post(reverse("oauth2_token"), data) + self.assertExceptionJson(request, ClientCredentialsNotProvided()) + + data["password"] = "not valid" + + request = self.client.post(reverse("oauth2_token"), data) + self.assertExceptionJson(request, ClientCredentialsNotValid()) + class TestTokenResponse(TokenTestCase):