From be741e79e3fa59f1b95d91345d5dd3c5fc8e9b39 Mon Sep 17 00:00:00 2001 From: juanifioren Date: Wed, 28 Jan 2015 15:19:36 -0300 Subject: [PATCH] Pull request PEP8 compliant from nicchub:master. --- README.rst | 2 + openid_provider/lib/endpoints/authorize.py | 40 ++++++++--------- openid_provider/lib/endpoints/token.py | 26 ++++++----- openid_provider/lib/endpoints/userinfo.py | 12 ++--- openid_provider/lib/errors.py | 52 ++++++++++++++++++---- openid_provider/lib/scopes.py | 9 ++-- openid_provider/lib/utils/params.py | 4 +- openid_provider/lib/utils/token.py | 26 ++++++----- openid_provider/models.py | 12 ++++- openid_provider/urls.py | 8 ++-- openid_provider/views.py | 5 ++- 11 files changed, 125 insertions(+), 71 deletions(-) diff --git a/README.rst b/README.rst index f613a93..170d286 100644 --- a/README.rst +++ b/README.rst @@ -50,6 +50,8 @@ Add the provider urls. Settings ******** +Add required variables to your project settings. + .. code:: python # REQUIRED. Your server provider url. diff --git a/openid_provider/lib/endpoints/authorize.py b/openid_provider/lib/endpoints/authorize.py index f12ac97..2a9dbd5 100644 --- a/openid_provider/lib/endpoints/authorize.py +++ b/openid_provider/lib/endpoints/authorize.py @@ -1,10 +1,13 @@ +import uuid + from datetime import timedelta + from django.utils import timezone + from openid_provider.lib.errors import * from openid_provider.lib.utils.params import * from openid_provider.lib.utils.token import * from openid_provider.models import * -import uuid class AuthorizeEndpoint(object): @@ -17,8 +20,7 @@ class AuthorizeEndpoint(object): # Because in this endpoint we handle both GET # and POST request. - self.query_dict = (self.request.POST if self.request.method == 'POST' - else self.request.GET) + self.query_dict = (self.request.POST if self.request.method == 'POST' else self.request.GET) self._extract_params() @@ -32,12 +34,12 @@ class AuthorizeEndpoint(object): self.grant_type = None def _extract_params(self): - ''' + """ Get all the params used by the Authorization Code Flow (and also for the Implicit). See: http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest - ''' + """ self.params.client_id = self.query_dict.get('client_id', '') self.params.redirect_uri = self.query_dict.get('redirect_uri', '') self.params.response_type = self.query_dict.get('response_type', '') @@ -45,11 +47,11 @@ class AuthorizeEndpoint(object): self.params.state = self.query_dict.get('state', '') def _extract_implicit_params(self): - ''' + """ Get specific params used by the Implicit Flow. See: http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest - ''' + """ self.params.nonce = self.query_dict.get('nonce', '') def validate_params(self): @@ -69,8 +71,7 @@ class AuthorizeEndpoint(object): if not (self.params.redirect_uri in self.client.redirect_uris): raise RedirectUriError() - if not (self.grant_type) or \ - not (self.params.response_type == self.client.response_type): + if not self.grant_type or not (self.params.response_type == self.client.response_type): raise AuthorizeError( self.params.redirect_uri, @@ -91,23 +92,23 @@ class AuthorizeEndpoint(object): try: self.validate_params() - if (self.grant_type == 'authorization_code'): + if self.grant_type == 'authorization_code': code = Code() code.user = self.request.user code.client = self.client code.code = uuid.uuid4().hex - code.expires_at = timezone.now() + timedelta(seconds=60*10) # TODO: Add this into settings. + code.expires_at = timezone.now() + timedelta(seconds=60*10) # TODO: Add this into settings. code.scope = self.params.scope code.save() uri = self.params.redirect_uri + '?code={0}'.format(code.code) - else: # Implicit Flow + else: # Implicit Flow id_token_dic = create_id_token_dic( self.request.user, - 'http://localhost:8000', # TODO: Add this into settings. + 'http://localhost:8000', # TODO: Add this into settings. self.client.client_id) token = create_token( @@ -123,11 +124,11 @@ class AuthorizeEndpoint(object): # TODO: Check if response_type is 'id_token token' then # add access_token to the fragment. - uri = self.params.redirect_uri + \ - '#token_type={0}&id_token={1}&expires_in={2}'.format( - 'bearer', - id_token, - 60*10) + uri = self.params.redirect_uri + '#token_type={0}&id_token={1}&expires_in={2}'.format( + 'bearer', + id_token, + 60*10 + ) except: raise AuthorizeError( self.params.redirect_uri, @@ -135,7 +136,6 @@ class AuthorizeEndpoint(object): self.grant_type) # Add state if present. - uri = uri + ('&state={0}'.format(self.params.state) - if self.params.state else '') + uri = uri + ('&state={0}'.format(self.params.state) if self.params.state else '') return uri \ No newline at end of file diff --git a/openid_provider/lib/endpoints/token.py b/openid_provider/lib/endpoints/token.py index ff1fa58..f64d325 100644 --- a/openid_provider/lib/endpoints/token.py +++ b/openid_provider/lib/endpoints/token.py @@ -1,10 +1,12 @@ +import urllib + from django.http import JsonResponse + from openid_provider.lib.errors import * from openid_provider.lib.utils.params import * from openid_provider.lib.utils.token import * from openid_provider.models import * from openid_provider import settings -import urllib class TokenEndpoint(object): @@ -54,15 +56,15 @@ class TokenEndpoint(object): def create_response_dic(self): id_token_dic = create_id_token_dic( - self.code.user, - settings.get('SITE_URL'), - self.client.client_id) + self.code.user, + settings.get('SITE_URL'), + self.client.client_id) token = create_token( - user=self.code.user, - client=self.code.client, - id_token_dic=id_token_dic, - scope=self.code.scope) + user=self.code.user, + client=self.code.client, + id_token_dic=id_token_dic, + scope=self.code.scope) # Store the token. token.save() @@ -75,7 +77,7 @@ class TokenEndpoint(object): dic = { 'access_token': token.access_token, 'token_type': 'bearer', - 'expires_in': 60*60, # TODO: Add this into settings. + 'expires_in': 60*60, # TODO: Add this into settings. 'id_token': id_token, } @@ -83,11 +85,11 @@ class TokenEndpoint(object): @classmethod def response(self, dic, status=200): - ''' + """ Create and return a response object. - ''' + """ response = JsonResponse(dic, status=status) response['Cache-Control'] = 'no-store' response['Pragma'] = 'no-cache' - return response \ No newline at end of file + return response diff --git a/openid_provider/lib/endpoints/userinfo.py b/openid_provider/lib/endpoints/userinfo.py index f9377c4..84b56ab 100644 --- a/openid_provider/lib/endpoints/userinfo.py +++ b/openid_provider/lib/endpoints/userinfo.py @@ -1,9 +1,11 @@ +import re + from django.http import HttpResponse, JsonResponse + from openid_provider.lib.errors import * from openid_provider.lib.scopes import * from openid_provider.lib.utils.params import * from openid_provider.models import * -import re class UserInfoEndpoint(object): @@ -21,12 +23,12 @@ class UserInfoEndpoint(object): self.params.access_token = self._get_access_token() def _get_access_token(self): - ''' + """ Get the access token using Authorization Request Header Field method. See: http://tools.ietf.org/html/rfc6750#section-2.1 Return a string. - ''' + """ auth_header = self.request.META.get('HTTP_AUTHORIZATION', '') if re.compile('^Bearer\s{1}.+$').match(auth_header): @@ -45,12 +47,12 @@ class UserInfoEndpoint(object): raise UserInfoError('invalid_token') def create_response_dic(self): - ''' + """ Create a diccionary with all the requested claims about the End-User. See: http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse Return a diccionary. - ''' + """ dic = { 'sub': self.token.id_token.get('sub'), } diff --git a/openid_provider/lib/errors.py b/openid_provider/lib/errors.py index 08d5627..51ba9f3 100644 --- a/openid_provider/lib/errors.py +++ b/openid_provider/lib/errors.py @@ -6,33 +6,52 @@ class RedirectUriError(Exception): error = 'Redirect URI Error' description = 'The request fails due to a missing, invalid, or mismatching redirection URI (redirect_uri).' + class ClientIdError(Exception): error = 'Client ID Error' description = 'The client identifier (client_id) is missing or invalid.' + class AuthorizeError(Exception): _errors = { # Oauth2 errors. # https://tools.ietf.org/html/rfc6749#section-4.1.2.1 'invalid_request': 'The request is otherwise malformed', + 'unauthorized_client': 'The client is not authorized to request an authorization code using this method', + 'access_denied': 'The resource owner or authorization server denied the request', - 'unsupported_response_type': 'The authorization server does not support obtaining an authorization code using this method', + + 'unsupported_response_type': 'The authorization server does not support obtaining an authorization code using ' + 'this method', + 'invalid_scope': 'The requested scope is invalid, unknown, or malformed', + 'server_error': 'The authorization server encountered an error', - 'temporarily_unavailable': 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server', + + 'temporarily_unavailable': 'The authorization server is currently unable to handle the request due to a ' + 'temporary overloading or maintenance of the server', + # OpenID errors. # http://openid.net/specs/openid-connect-core-1_0.html#AuthError 'interaction_required': 'The Authorization Server requires End-User interaction of some form to proceed', + 'login_required': 'The Authorization Server requires End-User authentication', + 'account_selection_required': 'The End-User is required to select a session at the Authorization Server', + 'consent_required': 'The Authorization Server requires End-User consent', + 'invalid_request_uri': 'The request_uri in the Authorization Request returns an error or contains invalid data', + 'invalid_request_object': 'The request parameter contains an invalid Request Object', + 'request_not_supported': 'The provider does not support use of the request parameter', + 'request_uri_not_supported': 'The provider does not support use of the request_uri parameter', + 'registration_not_supported': 'The provider does not support use of the registration parameter', } @@ -65,17 +84,26 @@ class AuthorizeError(Exception): def response(self): pass + class TokenError(Exception): _errors = { # Oauth2 errors. # https://tools.ietf.org/html/rfc6749#section-5.2 'invalid_request': 'The request is otherwise malformed', - 'invalid_client': 'Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)', - 'invalid_grant': 'The provided authorization grant or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client', + + 'invalid_client': 'Client authentication failed (e.g., unknown client, no client authentication included, ' + 'or unsupported authentication method)', + + 'invalid_grant': 'The provided authorization grant or refresh token is invalid, expired, revoked, does not ' + 'match the redirection URI used in the authorization request, or was issued to another client', + 'unauthorized_client': 'The authenticated client is not authorized to use this authorization grant type', + 'unsupported_grant_type': 'The authorization grant type is not supported by the authorization server', - 'invalid_scope': 'The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the resource owner', + + 'invalid_scope': 'The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the ' + 'resource owner', } def __init__(self, error): @@ -92,14 +120,20 @@ class TokenError(Exception): return dic -class UserInfoError(Exception): +class UserInfoError(Exception): _errors = { # Oauth2 errors. # https://tools.ietf.org/html/rfc6750#section-3.1 - 'invalid_request': ('The request is otherwise malformed', 400), - 'invalid_token': ('The access token provided is expired, revoked, malformed, or invalid for other reasons', 401), - 'insufficient_scope': ('The request requires higher privileges than provided by the access token', 403), + 'invalid_request': ( + 'The request is otherwise malformed', 400 + ), + 'invalid_token': ( + 'The access token provided is expired, revoked, malformed, or invalid for other reasons', 401 + ), + 'insufficient_scope': ( + 'The request requires higher privileges than provided by the access token', 403 + ), } def __init__(self, code): diff --git a/openid_provider/lib/scopes.py b/openid_provider/lib/scopes.py index 0472d8d..530d0b7 100644 --- a/openid_provider/lib/scopes.py +++ b/openid_provider/lib/scopes.py @@ -1,4 +1,5 @@ from django.utils.translation import ugettext as _ + from openid_provider.models import UserInfo @@ -32,10 +33,10 @@ class StandardClaims(object): return dic def _scopes_registered(self): - ''' + """ Return a list that contains all the scopes registered in the class. - ''' + """ scopes = [] for name in self.__class__.__dict__: @@ -47,9 +48,9 @@ class StandardClaims(object): return scopes def _clean_dic(self, dic): - ''' + """ Clean recursively all empty or None values inside a dict. - ''' + """ aux_dic = dic.copy() for key, value in dic.iteritems(): diff --git a/openid_provider/lib/utils/params.py b/openid_provider/lib/utils/params.py index ca399ef..961424a 100644 --- a/openid_provider/lib/utils/params.py +++ b/openid_provider/lib/utils/params.py @@ -1,7 +1,7 @@ class Params(object): - ''' + """ The purpose of this class is for accesing params via dot notation. - ''' + """ pass \ No newline at end of file diff --git a/openid_provider/lib/utils/token.py b/openid_provider/lib/utils/token.py index 401627c..6adf7ed 100644 --- a/openid_provider/lib/utils/token.py +++ b/openid_provider/lib/utils/token.py @@ -1,19 +1,21 @@ -from datetime import timedelta -from django.utils import timezone -import jwt -from openid_provider.models import * import time +import jwt import uuid +from datetime import timedelta + +from django.utils import timezone +from openid_provider.models import * + def create_id_token_dic(user, iss, aud): - ''' + """ Receives a user object, iss (issuer) and aud (audience). Then creates the id_token dic. See: http://openid.net/specs/openid-connect-core-1_0.html#IDToken Return a dic. - ''' + """ expires_in = 60*10 now = timezone.now() @@ -34,22 +36,24 @@ def create_id_token_dic(user, iss, aud): return dic + def encode_id_token(id_token_dic, client_secret): - ''' + """ Represent the ID Token as a JSON Web Token (JWT). Return a hash. - ''' + """ id_token_hash = jwt.encode(id_token_dic, client_secret) return id_token_hash + def create_token(user, client, id_token_dic, scope): - ''' + """ Create and populate a Token object. Return a Token object. - ''' + """ token = Token() token.user = user token.client = client @@ -58,7 +62,7 @@ def create_token(user, client, id_token_dic, scope): token.id_token = id_token_dic token.refresh_token = uuid.uuid4().hex - token.expires_at = timezone.now() + timedelta(seconds=60*60) # TODO: Add this into settings. + token.expires_at = timezone.now() + timedelta(seconds=60*60) # TODO: Add this into settings. token.scope = scope return token \ No newline at end of file diff --git a/openid_provider/models.py b/openid_provider/models.py index c146f1c..ff0070c 100644 --- a/openid_provider/models.py +++ b/openid_provider/models.py @@ -1,7 +1,8 @@ -from django.contrib.auth.models import User +import json + from django.db import models from django.utils import timezone -import json +from django.contrib.auth.models import User class Client(models.Model): @@ -18,6 +19,7 @@ class Client(models.Model): response_type = models.CharField(max_length=30, choices=RESPONSE_TYPE_CHOICES) _redirect_uris = models.TextField(default='') + def redirect_uris(): def fget(self): return self._redirect_uris.splitlines() @@ -30,6 +32,7 @@ class Client(models.Model): def default_redirect_uri(self): return self.redirect_uris[0] if self.redirect_uris else '' + class Code(models.Model): user = models.ForeignKey(User) @@ -38,6 +41,7 @@ class Code(models.Model): expires_at = models.DateTimeField() _scope = models.TextField(default='') + def scope(): def fget(self): return self._scope.split() @@ -49,6 +53,7 @@ class Code(models.Model): def has_expired(self): return timezone.now() >= self.expires_at + class Token(models.Model): user = models.ForeignKey(User) @@ -57,6 +62,7 @@ class Token(models.Model): expires_at = models.DateTimeField() _scope = models.TextField(default='') + def scope(): def fget(self): return self._scope.split() @@ -66,6 +72,7 @@ class Token(models.Model): scope = property(**scope()) _id_token = models.TextField() + def id_token(): def fget(self): return json.loads(self._id_token) @@ -74,6 +81,7 @@ class Token(models.Model): return locals() id_token = property(**id_token()) + class UserInfo(models.Model): user = models.OneToOneField(User, primary_key=True) diff --git a/openid_provider/urls.py b/openid_provider/urls.py index c937e23..3b0183f 100644 --- a/openid_provider/urls.py +++ b/openid_provider/urls.py @@ -1,12 +1,12 @@ from django.conf.urls import patterns, include, url from django.views.decorators.csrf import csrf_exempt + from openid_provider.views import * urlpatterns = patterns('', - - url(r'^authorize/$', AuthorizeView.as_view(), name='authorize'), - url(r'^token/$', csrf_exempt(TokenView.as_view()), name='token'), - url(r'^userinfo/$', csrf_exempt(userinfo), name='userinfo'), + url(r'^authorize/$', AuthorizeView.as_view(), name='authorize'), + url(r'^token/$', csrf_exempt(TokenView.as_view()), name='token'), + url(r'^userinfo/$', csrf_exempt(userinfo), name='userinfo'), ) \ No newline at end of file diff --git a/openid_provider/views.py b/openid_provider/views.py index ee23629..0db4eed 100644 --- a/openid_provider/views.py +++ b/openid_provider/views.py @@ -5,7 +5,7 @@ from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import render from django.views.decorators.http import require_http_methods from django.views.generic import View -import urllib + from openid_provider.lib.errors import * from openid_provider.lib.endpoints.authorize import * from openid_provider.lib.endpoints.token import * @@ -69,6 +69,7 @@ class AuthorizeView(View): return HttpResponseRedirect(uri) + class TokenView(View): def post(self, request, *args, **kwargs): @@ -101,4 +102,4 @@ def userinfo(request): return UserInfoEndpoint.error_response( error.code, error.description, - error.status) \ No newline at end of file + error.status)