Pull request PEP8 compliant from nicchub:master.

This commit is contained in:
juanifioren 2015-01-28 15:19:36 -03:00
parent c323b0829b
commit be741e79e3
11 changed files with 125 additions and 71 deletions

View file

@ -50,6 +50,8 @@ Add the provider urls.
Settings Settings
******** ********
Add required variables to your project settings.
.. code:: python .. code:: python
# REQUIRED. Your server provider url. # REQUIRED. Your server provider url.

View file

@ -1,10 +1,13 @@
import uuid
from datetime import timedelta from datetime import timedelta
from django.utils import timezone from django.utils import timezone
from openid_provider.lib.errors import * from openid_provider.lib.errors import *
from openid_provider.lib.utils.params import * from openid_provider.lib.utils.params import *
from openid_provider.lib.utils.token import * from openid_provider.lib.utils.token import *
from openid_provider.models import * from openid_provider.models import *
import uuid
class AuthorizeEndpoint(object): class AuthorizeEndpoint(object):
@ -17,8 +20,7 @@ class AuthorizeEndpoint(object):
# Because in this endpoint we handle both GET # Because in this endpoint we handle both GET
# and POST request. # and POST request.
self.query_dict = (self.request.POST if self.request.method == 'POST' self.query_dict = (self.request.POST if self.request.method == 'POST' else self.request.GET)
else self.request.GET)
self._extract_params() self._extract_params()
@ -32,12 +34,12 @@ class AuthorizeEndpoint(object):
self.grant_type = None self.grant_type = None
def _extract_params(self): def _extract_params(self):
''' """
Get all the params used by the Authorization Code Flow Get all the params used by the Authorization Code Flow
(and also for the Implicit). (and also for the Implicit).
See: http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest See: http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
''' """
self.params.client_id = self.query_dict.get('client_id', '') self.params.client_id = self.query_dict.get('client_id', '')
self.params.redirect_uri = self.query_dict.get('redirect_uri', '') self.params.redirect_uri = self.query_dict.get('redirect_uri', '')
self.params.response_type = self.query_dict.get('response_type', '') 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', '') self.params.state = self.query_dict.get('state', '')
def _extract_implicit_params(self): def _extract_implicit_params(self):
''' """
Get specific params used by the Implicit Flow. Get specific params used by the Implicit Flow.
See: http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest See: http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest
''' """
self.params.nonce = self.query_dict.get('nonce', '') self.params.nonce = self.query_dict.get('nonce', '')
def validate_params(self): def validate_params(self):
@ -69,8 +71,7 @@ class AuthorizeEndpoint(object):
if not (self.params.redirect_uri in self.client.redirect_uris): if not (self.params.redirect_uri in self.client.redirect_uris):
raise RedirectUriError() raise RedirectUriError()
if not (self.grant_type) or \ if not self.grant_type or not (self.params.response_type == self.client.response_type):
not (self.params.response_type == self.client.response_type):
raise AuthorizeError( raise AuthorizeError(
self.params.redirect_uri, self.params.redirect_uri,
@ -91,23 +92,23 @@ class AuthorizeEndpoint(object):
try: try:
self.validate_params() self.validate_params()
if (self.grant_type == 'authorization_code'): if self.grant_type == 'authorization_code':
code = Code() code = Code()
code.user = self.request.user code.user = self.request.user
code.client = self.client code.client = self.client
code.code = uuid.uuid4().hex 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.scope = self.params.scope
code.save() code.save()
uri = self.params.redirect_uri + '?code={0}'.format(code.code) uri = self.params.redirect_uri + '?code={0}'.format(code.code)
else: # Implicit Flow else: # Implicit Flow
id_token_dic = create_id_token_dic( id_token_dic = create_id_token_dic(
self.request.user, self.request.user,
'http://localhost:8000', # TODO: Add this into settings. 'http://localhost:8000', # TODO: Add this into settings.
self.client.client_id) self.client.client_id)
token = create_token( token = create_token(
@ -123,11 +124,11 @@ class AuthorizeEndpoint(object):
# TODO: Check if response_type is 'id_token token' then # TODO: Check if response_type is 'id_token token' then
# add access_token to the fragment. # add access_token to the fragment.
uri = self.params.redirect_uri + \ uri = self.params.redirect_uri + '#token_type={0}&id_token={1}&expires_in={2}'.format(
'#token_type={0}&id_token={1}&expires_in={2}'.format( 'bearer',
'bearer', id_token,
id_token, 60*10
60*10) )
except: except:
raise AuthorizeError( raise AuthorizeError(
self.params.redirect_uri, self.params.redirect_uri,
@ -135,7 +136,6 @@ class AuthorizeEndpoint(object):
self.grant_type) self.grant_type)
# Add state if present. # Add state if present.
uri = uri + ('&state={0}'.format(self.params.state) uri = uri + ('&state={0}'.format(self.params.state) if self.params.state else '')
if self.params.state else '')
return uri return uri

View file

@ -1,10 +1,12 @@
import urllib
from django.http import JsonResponse from django.http import JsonResponse
from openid_provider.lib.errors import * from openid_provider.lib.errors import *
from openid_provider.lib.utils.params import * from openid_provider.lib.utils.params import *
from openid_provider.lib.utils.token import * from openid_provider.lib.utils.token import *
from openid_provider.models import * from openid_provider.models import *
from openid_provider import settings from openid_provider import settings
import urllib
class TokenEndpoint(object): class TokenEndpoint(object):
@ -54,15 +56,15 @@ class TokenEndpoint(object):
def create_response_dic(self): def create_response_dic(self):
id_token_dic = create_id_token_dic( id_token_dic = create_id_token_dic(
self.code.user, self.code.user,
settings.get('SITE_URL'), settings.get('SITE_URL'),
self.client.client_id) self.client.client_id)
token = create_token( token = create_token(
user=self.code.user, user=self.code.user,
client=self.code.client, client=self.code.client,
id_token_dic=id_token_dic, id_token_dic=id_token_dic,
scope=self.code.scope) scope=self.code.scope)
# Store the token. # Store the token.
token.save() token.save()
@ -75,7 +77,7 @@ class TokenEndpoint(object):
dic = { dic = {
'access_token': token.access_token, 'access_token': token.access_token,
'token_type': 'bearer', '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, 'id_token': id_token,
} }
@ -83,11 +85,11 @@ class TokenEndpoint(object):
@classmethod @classmethod
def response(self, dic, status=200): def response(self, dic, status=200):
''' """
Create and return a response object. Create and return a response object.
''' """
response = JsonResponse(dic, status=status) response = JsonResponse(dic, status=status)
response['Cache-Control'] = 'no-store' response['Cache-Control'] = 'no-store'
response['Pragma'] = 'no-cache' response['Pragma'] = 'no-cache'
return response return response

View file

@ -1,9 +1,11 @@
import re
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse, JsonResponse
from openid_provider.lib.errors import * from openid_provider.lib.errors import *
from openid_provider.lib.scopes import * from openid_provider.lib.scopes import *
from openid_provider.lib.utils.params import * from openid_provider.lib.utils.params import *
from openid_provider.models import * from openid_provider.models import *
import re
class UserInfoEndpoint(object): class UserInfoEndpoint(object):
@ -21,12 +23,12 @@ class UserInfoEndpoint(object):
self.params.access_token = self._get_access_token() self.params.access_token = self._get_access_token()
def _get_access_token(self): def _get_access_token(self):
''' """
Get the access token using Authorization Request Header Field method. Get the access token using Authorization Request Header Field method.
See: http://tools.ietf.org/html/rfc6750#section-2.1 See: http://tools.ietf.org/html/rfc6750#section-2.1
Return a string. Return a string.
''' """
auth_header = self.request.META.get('HTTP_AUTHORIZATION', '') auth_header = self.request.META.get('HTTP_AUTHORIZATION', '')
if re.compile('^Bearer\s{1}.+$').match(auth_header): if re.compile('^Bearer\s{1}.+$').match(auth_header):
@ -45,12 +47,12 @@ class UserInfoEndpoint(object):
raise UserInfoError('invalid_token') raise UserInfoError('invalid_token')
def create_response_dic(self): def create_response_dic(self):
''' """
Create a diccionary with all the requested claims about the End-User. Create a diccionary with all the requested claims about the End-User.
See: http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse See: http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
Return a diccionary. Return a diccionary.
''' """
dic = { dic = {
'sub': self.token.id_token.get('sub'), 'sub': self.token.id_token.get('sub'),
} }

View file

@ -6,33 +6,52 @@ class RedirectUriError(Exception):
error = 'Redirect URI Error' error = 'Redirect URI Error'
description = 'The request fails due to a missing, invalid, or mismatching redirection URI (redirect_uri).' description = 'The request fails due to a missing, invalid, or mismatching redirection URI (redirect_uri).'
class ClientIdError(Exception): class ClientIdError(Exception):
error = 'Client ID Error' error = 'Client ID Error'
description = 'The client identifier (client_id) is missing or invalid.' description = 'The client identifier (client_id) is missing or invalid.'
class AuthorizeError(Exception): class AuthorizeError(Exception):
_errors = { _errors = {
# Oauth2 errors. # Oauth2 errors.
# https://tools.ietf.org/html/rfc6749#section-4.1.2.1 # https://tools.ietf.org/html/rfc6749#section-4.1.2.1
'invalid_request': 'The request is otherwise malformed', 'invalid_request': 'The request is otherwise malformed',
'unauthorized_client': 'The client is not authorized to request an authorization code using this method', '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', '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', 'invalid_scope': 'The requested scope is invalid, unknown, or malformed',
'server_error': 'The authorization server encountered an error', '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. # OpenID errors.
# http://openid.net/specs/openid-connect-core-1_0.html#AuthError # 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', 'interaction_required': 'The Authorization Server requires End-User interaction of some form to proceed',
'login_required': 'The Authorization Server requires End-User authentication', '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', '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', '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_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', '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_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', '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', 'registration_not_supported': 'The provider does not support use of the registration parameter',
} }
@ -65,17 +84,26 @@ class AuthorizeError(Exception):
def response(self): def response(self):
pass pass
class TokenError(Exception): class TokenError(Exception):
_errors = { _errors = {
# Oauth2 errors. # Oauth2 errors.
# https://tools.ietf.org/html/rfc6749#section-5.2 # https://tools.ietf.org/html/rfc6749#section-5.2
'invalid_request': 'The request is otherwise malformed', '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', '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', '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): def __init__(self, error):
@ -92,14 +120,20 @@ class TokenError(Exception):
return dic return dic
class UserInfoError(Exception):
class UserInfoError(Exception):
_errors = { _errors = {
# Oauth2 errors. # Oauth2 errors.
# https://tools.ietf.org/html/rfc6750#section-3.1 # https://tools.ietf.org/html/rfc6750#section-3.1
'invalid_request': ('The request is otherwise malformed', 400), 'invalid_request': (
'invalid_token': ('The access token provided is expired, revoked, malformed, or invalid for other reasons', 401), 'The request is otherwise malformed', 400
'insufficient_scope': ('The request requires higher privileges than provided by the access token', 403), ),
'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): def __init__(self, code):

View file

@ -1,4 +1,5 @@
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from openid_provider.models import UserInfo from openid_provider.models import UserInfo
@ -32,10 +33,10 @@ class StandardClaims(object):
return dic return dic
def _scopes_registered(self): def _scopes_registered(self):
''' """
Return a list that contains all the scopes registered Return a list that contains all the scopes registered
in the class. in the class.
''' """
scopes = [] scopes = []
for name in self.__class__.__dict__: for name in self.__class__.__dict__:
@ -47,9 +48,9 @@ class StandardClaims(object):
return scopes return scopes
def _clean_dic(self, dic): def _clean_dic(self, dic):
''' """
Clean recursively all empty or None values inside a dict. Clean recursively all empty or None values inside a dict.
''' """
aux_dic = dic.copy() aux_dic = dic.copy()
for key, value in dic.iteritems(): for key, value in dic.iteritems():

View file

@ -1,7 +1,7 @@
class Params(object): class Params(object):
''' """
The purpose of this class is for accesing params via dot notation. The purpose of this class is for accesing params via dot notation.
''' """
pass pass

View file

@ -1,19 +1,21 @@
from datetime import timedelta
from django.utils import timezone
import jwt
from openid_provider.models import *
import time import time
import jwt
import uuid import uuid
from datetime import timedelta
from django.utils import timezone
from openid_provider.models import *
def create_id_token_dic(user, iss, aud): def create_id_token_dic(user, iss, aud):
''' """
Receives a user object, iss (issuer) and aud (audience). Receives a user object, iss (issuer) and aud (audience).
Then creates the id_token dic. Then creates the id_token dic.
See: http://openid.net/specs/openid-connect-core-1_0.html#IDToken See: http://openid.net/specs/openid-connect-core-1_0.html#IDToken
Return a dic. Return a dic.
''' """
expires_in = 60*10 expires_in = 60*10
now = timezone.now() now = timezone.now()
@ -34,22 +36,24 @@ def create_id_token_dic(user, iss, aud):
return dic return dic
def encode_id_token(id_token_dic, client_secret): def encode_id_token(id_token_dic, client_secret):
''' """
Represent the ID Token as a JSON Web Token (JWT). Represent the ID Token as a JSON Web Token (JWT).
Return a hash. Return a hash.
''' """
id_token_hash = jwt.encode(id_token_dic, client_secret) id_token_hash = jwt.encode(id_token_dic, client_secret)
return id_token_hash return id_token_hash
def create_token(user, client, id_token_dic, scope): def create_token(user, client, id_token_dic, scope):
''' """
Create and populate a Token object. Create and populate a Token object.
Return a Token object. Return a Token object.
''' """
token = Token() token = Token()
token.user = user token.user = user
token.client = client token.client = client
@ -58,7 +62,7 @@ def create_token(user, client, id_token_dic, scope):
token.id_token = id_token_dic token.id_token = id_token_dic
token.refresh_token = uuid.uuid4().hex 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 token.scope = scope
return token return token

View file

@ -1,7 +1,8 @@
from django.contrib.auth.models import User import json
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
import json from django.contrib.auth.models import User
class Client(models.Model): class Client(models.Model):
@ -18,6 +19,7 @@ class Client(models.Model):
response_type = models.CharField(max_length=30, choices=RESPONSE_TYPE_CHOICES) response_type = models.CharField(max_length=30, choices=RESPONSE_TYPE_CHOICES)
_redirect_uris = models.TextField(default='') _redirect_uris = models.TextField(default='')
def redirect_uris(): def redirect_uris():
def fget(self): def fget(self):
return self._redirect_uris.splitlines() return self._redirect_uris.splitlines()
@ -30,6 +32,7 @@ class Client(models.Model):
def default_redirect_uri(self): def default_redirect_uri(self):
return self.redirect_uris[0] if self.redirect_uris else '' return self.redirect_uris[0] if self.redirect_uris else ''
class Code(models.Model): class Code(models.Model):
user = models.ForeignKey(User) user = models.ForeignKey(User)
@ -38,6 +41,7 @@ class Code(models.Model):
expires_at = models.DateTimeField() expires_at = models.DateTimeField()
_scope = models.TextField(default='') _scope = models.TextField(default='')
def scope(): def scope():
def fget(self): def fget(self):
return self._scope.split() return self._scope.split()
@ -49,6 +53,7 @@ class Code(models.Model):
def has_expired(self): def has_expired(self):
return timezone.now() >= self.expires_at return timezone.now() >= self.expires_at
class Token(models.Model): class Token(models.Model):
user = models.ForeignKey(User) user = models.ForeignKey(User)
@ -57,6 +62,7 @@ class Token(models.Model):
expires_at = models.DateTimeField() expires_at = models.DateTimeField()
_scope = models.TextField(default='') _scope = models.TextField(default='')
def scope(): def scope():
def fget(self): def fget(self):
return self._scope.split() return self._scope.split()
@ -66,6 +72,7 @@ class Token(models.Model):
scope = property(**scope()) scope = property(**scope())
_id_token = models.TextField() _id_token = models.TextField()
def id_token(): def id_token():
def fget(self): def fget(self):
return json.loads(self._id_token) return json.loads(self._id_token)
@ -74,6 +81,7 @@ class Token(models.Model):
return locals() return locals()
id_token = property(**id_token()) id_token = property(**id_token())
class UserInfo(models.Model): class UserInfo(models.Model):
user = models.OneToOneField(User, primary_key=True) user = models.OneToOneField(User, primary_key=True)

View file

@ -1,12 +1,12 @@
from django.conf.urls import patterns, include, url from django.conf.urls import patterns, include, url
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from openid_provider.views import * from openid_provider.views import *
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^authorize/$', AuthorizeView.as_view(), name='authorize'),
url(r'^authorize/$', AuthorizeView.as_view(), name='authorize'), url(r'^token/$', csrf_exempt(TokenView.as_view()), name='token'),
url(r'^token/$', csrf_exempt(TokenView.as_view()), name='token'), url(r'^userinfo/$', csrf_exempt(userinfo), name='userinfo'),
url(r'^userinfo/$', csrf_exempt(userinfo), name='userinfo'),
) )

View file

@ -5,7 +5,7 @@ from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render from django.shortcuts import render
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django.views.generic import View from django.views.generic import View
import urllib
from openid_provider.lib.errors import * from openid_provider.lib.errors import *
from openid_provider.lib.endpoints.authorize import * from openid_provider.lib.endpoints.authorize import *
from openid_provider.lib.endpoints.token import * from openid_provider.lib.endpoints.token import *
@ -69,6 +69,7 @@ class AuthorizeView(View):
return HttpResponseRedirect(uri) return HttpResponseRedirect(uri)
class TokenView(View): class TokenView(View):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
@ -101,4 +102,4 @@ def userinfo(request):
return UserInfoEndpoint.error_response( return UserInfoEndpoint.error_response(
error.code, error.code,
error.description, error.description,
error.status) error.status)