diff --git a/CHANGELOG.md b/CHANGELOG.md index ae386af..7a2b927 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. ### [Unreleased] +##### Added +- Choose type of client on creation. + +### [0.3.1] - 2016-03-09 + +##### Fixed +- response_type was not being validated (OpenID request). + ### [0.3.0] - 2016-02-23 ##### Added diff --git a/LICENSE b/LICENSE index cc208c6..eef5213 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Juan Ignacio Fiorentino +Copyright (c) 2014-2016 Juan Ignacio Fiorentino Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/oidc_provider/admin.py b/oidc_provider/admin.py index 9963b35..2a4fc61 100644 --- a/oidc_provider/admin.py +++ b/oidc_provider/admin.py @@ -30,10 +30,19 @@ class ClientForm(ModelForm): def clean_client_secret(self): instance = getattr(self, 'instance', None) + + secret = '' + if instance and instance.pk: - return instance.client_secret + if (self.cleaned_data['client_type'] == 'confidential') and not instance.client_secret: + secret = md5(uuid4().hex.encode()).hexdigest() + elif (self.cleaned_data['client_type'] == 'confidential') and instance.client_secret: + secret = instance.client_secret else: - return md5(uuid4().hex.encode()).hexdigest() + if (instance.client_type == 'confidential'): + secret = md5(uuid4().hex.encode()).hexdigest() + + return secret @admin.register(Client) diff --git a/oidc_provider/lib/endpoints/authorize.py b/oidc_provider/lib/endpoints/authorize.py index f571c81..56972a4 100644 --- a/oidc_provider/lib/endpoints/authorize.py +++ b/oidc_provider/lib/endpoints/authorize.py @@ -60,15 +60,15 @@ class AuthorizeEndpoint(object): try: self.client = Client.objects.get(client_id=self.params.client_id) except Client.DoesNotExist: - logger.error('[Authorize] Invalid client identifier: %s', self.params.client_id) + logger.debug('[Authorize] Invalid client identifier: %s', self.params.client_id) raise ClientIdError() if self.is_authentication and not self.params.redirect_uri: - logger.error('[Authorize] Missing redirect uri.') + logger.debug('[Authorize] Missing redirect uri.') raise RedirectUriError() if not self.grant_type: - logger.error('[Authorize] Invalid response type: %s', self.params.response_type) + logger.debug('[Authorize] Invalid response type: %s', self.params.response_type) raise AuthorizeError(self.params.redirect_uri, 'unsupported_response_type', self.grant_type) @@ -83,7 +83,7 @@ class AuthorizeEndpoint(object): clean_redirect_uri = urlsplit(self.params.redirect_uri) clean_redirect_uri = urlunsplit(clean_redirect_uri._replace(query='')) if not (clean_redirect_uri in self.client.redirect_uris): - logger.error('[Authorize] Invalid redirect uri: %s', self.params.redirect_uri) + logger.debug('[Authorize] Invalid redirect uri: %s', self.params.redirect_uri) raise RedirectUriError() @@ -138,7 +138,7 @@ class AuthorizeEndpoint(object): query_fragment['state'] = self.params.state if self.params.state else '' except Exception as error: - logger.error('[Authorize] Error when trying to create response uri: %s', error) + logger.debug('[Authorize] Error when trying to create response uri: %s', error) raise AuthorizeError( self.params.redirect_uri, 'server_error', diff --git a/oidc_provider/lib/endpoints/token.py b/oidc_provider/lib/endpoints/token.py index a134f4f..a981eee 100644 --- a/oidc_provider/lib/endpoints/token.py +++ b/oidc_provider/lib/endpoints/token.py @@ -65,34 +65,34 @@ class TokenEndpoint(object): try: self.client = Client.objects.get(client_id=self.params.client_id) except Client.DoesNotExist: - logger.error('[Token] Client does not exist: %s', self.params.client_id) + logger.debug('[Token] Client does not exist: %s', self.params.client_id) raise TokenError('invalid_client') if not (self.client.client_secret == self.params.client_secret): - logger.error('[Token] Invalid client secret: client %s do not have secret %s', + logger.debug('[Token] Invalid client secret: client %s do not have secret %s', self.client.client_id, self.client.client_secret) raise TokenError('invalid_client') if self.params.grant_type == 'authorization_code': if not (self.params.redirect_uri in self.client.redirect_uris): - logger.error('[Token] Invalid redirect uri: %s', self.params.redirect_uri) + logger.debug('[Token] Invalid redirect uri: %s', self.params.redirect_uri) raise TokenError('invalid_client') try: self.code = Code.objects.get(code=self.params.code) except Code.DoesNotExist: - logger.error('[Token] Code does not exist: %s', self.params.code) + logger.debug('[Token] Code does not exist: %s', self.params.code) raise TokenError('invalid_grant') if not (self.code.client == self.client) \ or self.code.has_expired(): - logger.error('[Token] Invalid code: invalid client or code has expired', + logger.debug('[Token] Invalid code: invalid client or code has expired', self.params.redirect_uri) raise TokenError('invalid_grant') elif self.params.grant_type == 'refresh_token': if not self.params.refresh_token: - logger.error('[Token] Missing refresh token') + logger.debug('[Token] Missing refresh token') raise TokenError('invalid_grant') try: @@ -100,11 +100,11 @@ class TokenEndpoint(object): client=self.client) except Token.DoesNotExist: - logger.error('[Token] Refresh token does not exist: %s', self.params.refresh_token) + logger.debug('[Token] Refresh token does not exist: %s', self.params.refresh_token) raise TokenError('invalid_grant') else: - logger.error('[Token] Invalid grant type: %s', self.params.grant_type) + logger.debug('[Token] Invalid grant type: %s', self.params.grant_type) raise TokenError('unsupported_grant_type') def create_response_dic(self): diff --git a/oidc_provider/lib/utils/oauth2.py b/oidc_provider/lib/utils/oauth2.py index 9d54e75..eba482c 100644 --- a/oidc_provider/lib/utils/oauth2.py +++ b/oidc_provider/lib/utils/oauth2.py @@ -42,15 +42,15 @@ def protected_resource_view(scopes=[]): try: kwargs['token'] = Token.objects.get(access_token=access_token) except Token.DoesNotExist: - logger.error('[UserInfo] Token does not exist: %s', access_token) + logger.debug('[UserInfo] Token does not exist: %s', access_token) raise BearerTokenError('invalid_token') if kwargs['token'].has_expired(): - logger.error('[UserInfo] Token has expired: %s', access_token) + logger.debug('[UserInfo] Token has expired: %s', access_token) raise BearerTokenError('invalid_token') if not set(scopes).issubset(set(kwargs['token'].scope)): - logger.error('[UserInfo] Missing openid scope.') + logger.debug('[UserInfo] Missing openid scope.') raise BearerTokenError('insufficient_scope') except (BearerTokenError) as error: response = HttpResponse(status=error.status) diff --git a/oidc_provider/migrations/0011_client_client_type.py b/oidc_provider/migrations/0011_client_client_type.py new file mode 100644 index 0000000..26e9fc3 --- /dev/null +++ b/oidc_provider/migrations/0011_client_client_type.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-04-04 19:56 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('oidc_provider', '0010_code_is_authentication'), + ] + + operations = [ + migrations.AddField( + model_name='client', + name='client_type', + field=models.CharField(choices=[(b'confidential', b'Confidential'), (b'public', b'Public')], default=b'confidential', help_text='Confidential clients are capable of maintaining the confidentiality of their credentials. Public clients are incapable.', max_length=30), + ), + ] diff --git a/oidc_provider/migrations/0012_auto_20160405_2041.py b/oidc_provider/migrations/0012_auto_20160405_2041.py new file mode 100644 index 0000000..c04b613 --- /dev/null +++ b/oidc_provider/migrations/0012_auto_20160405_2041.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-04-05 20:41 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('oidc_provider', '0011_client_client_type'), + ] + + operations = [ + migrations.AlterField( + model_name='client', + name='client_secret', + field=models.CharField(blank=True, default=b'', max_length=255), + ), + ] diff --git a/oidc_provider/models.py b/oidc_provider/models.py index 68ace73..8d9ad39 100644 --- a/oidc_provider/models.py +++ b/oidc_provider/models.py @@ -10,6 +10,11 @@ from django.conf import settings class Client(models.Model): + CLIENT_TYPE_CHOICES = [ + ('confidential', 'Confidential'), + ('public', 'Public'), + ] + RESPONSE_TYPE_CHOICES = [ ('code', 'code (Authorization Code Flow)'), ('id_token', 'id_token (Implicit Flow)'), @@ -17,10 +22,10 @@ class Client(models.Model): ] name = models.CharField(max_length=100, default='') + client_type = models.CharField(max_length=30, choices=CLIENT_TYPE_CHOICES, default='confidential', help_text=_(u'Confidential clients are capable of maintaining the confidentiality of their credentials. Public clients are incapable.')) client_id = models.CharField(max_length=255, unique=True) - client_secret = models.CharField(max_length=255, unique=True) - response_type = models.CharField(max_length=30, - choices=RESPONSE_TYPE_CHOICES) + client_secret = models.CharField(max_length=255, blank=True, default='') + response_type = models.CharField(max_length=30, choices=RESPONSE_TYPE_CHOICES) date_created = models.DateField(auto_now_add=True) _redirect_uris = models.TextField(default='', verbose_name=_(u'Redirect URI'), help_text=_(u'Enter each URI on a new line.')) diff --git a/setup.py b/setup.py index 66813ef..cb1a60e 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) setup( name='django-oidc-provider', - version='0.3.0', + version='0.3.1', packages=[ 'oidc_provider', 'oidc_provider/lib', 'oidc_provider/lib/endpoints', 'oidc_provider/lib/utils', 'oidc_provider/tests', 'oidc_provider/tests/app',