Skip to content
This repository was archived by the owner on Nov 27, 2017. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions doac/exceptions/invalid_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."

Expand Down
134 changes: 102 additions & 32 deletions doac/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,26 @@

ALLOWED_RESPONSE_TYPES = ("code", "token", )

ALLOWED_GRANT_TYPES = ("authorization_code", "refresh_token", )
ALLOWED_GRANT_TYPES = ("authorization_code", "refresh_token", "password", )


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)
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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.
"""
Expand All @@ -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:
Expand All @@ -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", )

Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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:
Expand All @@ -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)

Expand All @@ -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
Expand Down Expand Up @@ -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)

Expand All @@ -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()
Expand All @@ -329,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
Expand Down Expand Up @@ -356,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
Expand All @@ -367,12 +416,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()
Expand All @@ -384,7 +435,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)

Expand All @@ -395,13 +447,31 @@ 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:
raise RefreshTokenNotProvided()

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 ClientCredentialsNotProvided()

self.user = authenticate(username=username, password=password)

if not self.user or not self.user.is_active:
raise ClientCredentialsNotValid()
Loading