From c323b0829b9e10f48289668f677854189369c299 Mon Sep 17 00:00:00 2001 From: juanifioren Date: Wed, 28 Jan 2015 14:50:14 -0300 Subject: [PATCH 1/3] Add default package settings. Edit README. --- README.rst | 15 ++++++++++++--- openid_provider/lib/endpoints/token.py | 3 ++- openid_provider/settings.py | 23 +++++++++++++++++++++++ openid_provider/views.py | 6 +++--- 4 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 openid_provider/settings.py diff --git a/README.rst b/README.rst index 7707fa6..f613a93 100644 --- a/README.rst +++ b/README.rst @@ -46,9 +46,18 @@ Add the provider urls. # ... ) -Finally, add a login view and ensure that has the same url defined in `LOGIN_URL` setting. +******** +Settings +******** -See: https://docs.djangoproject.com/en/1.7/ref/settings/#login-url +.. code:: python + + # REQUIRED. Your server provider url. + SITE_URL = 'http://localhost:8000' + + # REQUIRED. + # See: https://docs.djangoproject.com/en/1.7/ref/settings/#login-url + LOGIN_URL = '/accounts/login/' ******************** Create User & Client @@ -61,7 +70,7 @@ Then let's create a Client. Start django shell: ``python manage.py shell``. .. code:: python >>> from openid_provider.models import Client - >>> c = Client(name='Some Client', client_id='123', client_secret='456', client_type='confidential', response_type='code', redirect_uris=['http://example.com/']) + >>> c = Client(name='Some Client', client_id='123', client_secret='456', response_type='code', redirect_uris=['http://example.com/']) >>> c.save() ******************* diff --git a/openid_provider/lib/endpoints/token.py b/openid_provider/lib/endpoints/token.py index 59661e6..ff1fa58 100644 --- a/openid_provider/lib/endpoints/token.py +++ b/openid_provider/lib/endpoints/token.py @@ -3,6 +3,7 @@ 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 @@ -54,7 +55,7 @@ class TokenEndpoint(object): id_token_dic = create_id_token_dic( self.code.user, - 'http://localhost:8000', # TODO: Add this into settings. + settings.get('SITE_URL'), self.client.client_id) token = create_token( diff --git a/openid_provider/settings.py b/openid_provider/settings.py new file mode 100644 index 0000000..caa7a4d --- /dev/null +++ b/openid_provider/settings.py @@ -0,0 +1,23 @@ +from django.conf import settings + + +class default_settings(object): + + # Here goes all the package default settings. + + LOGIN_URL = None + + SITE_URL = None + +def get(name): + ''' + Helper function to use inside the package. + ''' + try: + value = getattr(default_settings, name) + value = getattr(settings, name) + except AttributeError: + if value == None: + raise Exception('You must set ' + name + ' in your settings.') + + return value diff --git a/openid_provider/views.py b/openid_provider/views.py index d76c5ba..ee23629 100644 --- a/openid_provider/views.py +++ b/openid_provider/views.py @@ -1,4 +1,3 @@ -from django.conf import settings from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.views import redirect_to_login from django.core.urlresolvers import reverse @@ -11,6 +10,7 @@ from openid_provider.lib.errors import * from openid_provider.lib.endpoints.authorize import * from openid_provider.lib.endpoints.token import * from openid_provider.lib.endpoints.userinfo import * +from openid_provider import settings class AuthorizeView(View): @@ -24,7 +24,7 @@ class AuthorizeView(View): if request.user.is_authenticated(): - # This is for printing scopes in the form. + # This is for printing scopes in form. authorize.params.scope_str = ' '.join(authorize.params.scope) context = { @@ -36,7 +36,7 @@ class AuthorizeView(View): else: path = request.get_full_path() return redirect_to_login( - path, settings.LOGIN_URL, REDIRECT_FIELD_NAME) + path, settings.get('LOGIN_URL'), REDIRECT_FIELD_NAME) except (ClientIdError, RedirectUriError) as error: context = { From be741e79e3fa59f1b95d91345d5dd3c5fc8e9b39 Mon Sep 17 00:00:00 2001 From: juanifioren Date: Wed, 28 Jan 2015 15:19:36 -0300 Subject: [PATCH 2/3] 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) From 97eef517b0309a6531fff4525c5ca3b6d3821869 Mon Sep 17 00:00:00 2001 From: juanifioren Date: Wed, 28 Jan 2015 17:00:04 -0300 Subject: [PATCH 3/3] A few changes in settings. Add more variables to it. --- README.rst | 13 ++++++++-- openid_provider/lib/endpoints/authorize.py | 29 ++++++++++++---------- openid_provider/lib/endpoints/token.py | 12 ++++----- openid_provider/lib/utils/token.py | 16 ++++++------ openid_provider/settings.py | 17 +++++++------ openid_provider/views.py | 1 - 6 files changed, 50 insertions(+), 38 deletions(-) diff --git a/README.rst b/README.rst index 170d286..2f9167a 100644 --- a/README.rst +++ b/README.rst @@ -54,13 +54,22 @@ Add required variables to your project settings. .. code:: python - # REQUIRED. Your server provider url. + # REQUIRED. + + # Your server provider url. SITE_URL = 'http://localhost:8000' - # REQUIRED. + # Used to log the user in. # See: https://docs.djangoproject.com/en/1.7/ref/settings/#login-url LOGIN_URL = '/accounts/login/' + # OPTIONAL. + + DOP_CODE_EXPIRE = 60*10 # 10 min. + DOP_IDTOKEN_EXPIRE = 60*10, # 10 min. + DOP_TOKEN_EXPIRE = 60*60 # 1 hour. + + ******************** Create User & Client ******************** diff --git a/openid_provider/lib/endpoints/authorize.py b/openid_provider/lib/endpoints/authorize.py index 2a9dbd5..ebfbbb9 100644 --- a/openid_provider/lib/endpoints/authorize.py +++ b/openid_provider/lib/endpoints/authorize.py @@ -1,13 +1,11 @@ -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 * +from openid_provider import settings +import uuid class AuthorizeEndpoint(object): @@ -20,7 +18,8 @@ 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() @@ -91,14 +90,15 @@ class AuthorizeEndpoint(object): try: self.validate_params() - + 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=settings.get('DOP_CODE_EXPIRE')) code.scope = self.params.scope code.save() @@ -108,7 +108,7 @@ class AuthorizeEndpoint(object): id_token_dic = create_id_token_dic( self.request.user, - 'http://localhost:8000', # TODO: Add this into settings. + settings.get('SITE_URL'), self.client.client_id) token = create_token( @@ -120,14 +120,15 @@ class AuthorizeEndpoint(object): # Store the token. token.save() - id_token = encode_id_token(id_token_dic, self.client.client_secret) - + id_token = encode_id_token( + id_token_dic, self.client.client_secret) + # 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 + 60 * 10 ) except: raise AuthorizeError( @@ -136,6 +137,8 @@ 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 + return uri diff --git a/openid_provider/lib/endpoints/token.py b/openid_provider/lib/endpoints/token.py index f64d325..4718514 100644 --- a/openid_provider/lib/endpoints/token.py +++ b/openid_provider/lib/endpoints/token.py @@ -1,12 +1,10 @@ -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): @@ -23,7 +21,8 @@ class TokenEndpoint(object): self.params.client_id = query_dict.get('client_id', '') self.params.client_secret = query_dict.get('client_secret', '') - self.params.redirect_uri = urllib.unquote(query_dict.get('redirect_uri', '')) + self.params.redirect_uri = urllib.unquote( + query_dict.get('redirect_uri', '')) self.params.grant_type = query_dict.get('grant_type', '') self.params.code = query_dict.get('code', '') self.params.state = query_dict.get('state', '') @@ -44,7 +43,8 @@ class TokenEndpoint(object): self.code = Code.objects.get(code=self.params.code) - if not (self.code.client == self.client) and not self.code.has_expired(): + if not (self.code.client == self.client) and \ + not self.code.has_expired(): raise TokenError('invalid_grant') except Client.DoesNotExist: @@ -77,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': settings.get('DOP_TOKEN_EXPIRE'), 'id_token': id_token, } diff --git a/openid_provider/lib/utils/token.py b/openid_provider/lib/utils/token.py index 6adf7ed..dc2c859 100644 --- a/openid_provider/lib/utils/token.py +++ b/openid_provider/lib/utils/token.py @@ -1,11 +1,10 @@ -import time -import jwt -import uuid - from datetime import timedelta - from django.utils import timezone from openid_provider.models import * +from openid_provider import settings +import jwt +import time +import uuid def create_id_token_dic(user, iss, aud): @@ -16,7 +15,7 @@ def create_id_token_dic(user, iss, aud): Return a dic. """ - expires_in = 60*10 + expires_in = settings.get('DOP_IDTOKEN_EXPIRE') now = timezone.now() @@ -62,7 +61,8 @@ 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=settings.get('DOP_TOKEN_EXPIRE')) token.scope = scope - return token \ No newline at end of file + return token diff --git a/openid_provider/settings.py b/openid_provider/settings.py index caa7a4d..6accd8c 100644 --- a/openid_provider/settings.py +++ b/openid_provider/settings.py @@ -1,20 +1,21 @@ from django.conf import settings -class default_settings(object): - - # Here goes all the package default settings. - - LOGIN_URL = None - - SITE_URL = None +# Here goes all the package default settings. +default_settings = { + 'DOP_CODE_EXPIRE': 60*10, # 10 min. + 'DOP_IDTOKEN_EXPIRE': 60*10, # 10 min. + 'DOP_TOKEN_EXPIRE': 60*60, # 1 hour. + 'LOGIN_URL': None, + 'SITE_URL': None, +} def get(name): ''' Helper function to use inside the package. ''' try: - value = getattr(default_settings, name) + value = default_settings[name] value = getattr(settings, name) except AttributeError: if value == None: diff --git a/openid_provider/views.py b/openid_provider/views.py index 0db4eed..dfa3cbd 100644 --- a/openid_provider/views.py +++ b/openid_provider/views.py @@ -5,7 +5,6 @@ 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 - from openid_provider.lib.errors import * from openid_provider.lib.endpoints.authorize import * from openid_provider.lib.endpoints.token import *