diff --git a/oidc_provider/lib/endpoints/authorize.py b/oidc_provider/lib/endpoints/authorize.py index 43ff43f..602ff89 100644 --- a/oidc_provider/lib/endpoints/authorize.py +++ b/oidc_provider/lib/endpoints/authorize.py @@ -34,7 +34,7 @@ class AuthorizeEndpoint(object): self.grant_type = None # Determine if it's an OpenID Authentication request (or OAuth2). - self.is_authentication = 'openid' in self.params.scope + self.is_authentication = 'openid' in self.params.scope def _extract_params(self): """ @@ -53,7 +53,7 @@ class AuthorizeEndpoint(object): self.params.response_type = query_dict.get('response_type', '') self.params.scope = query_dict.get('scope', '').split() self.params.state = query_dict.get('state', '') - + self.params.nonce = query_dict.get('nonce', '') self.params.prompt = query_dict.get('prompt', '') self.params.code_challenge = query_dict.get('code_challenge', '') @@ -113,7 +113,7 @@ class AuthorizeEndpoint(object): is_authentication=self.is_authentication, code_challenge=self.params.code_challenge, code_challenge_method=self.params.code_challenge_method) - + code.save() query_params['code'] = code.code @@ -169,18 +169,24 @@ class AuthorizeEndpoint(object): Return None. """ - expires_at = timezone.now() + timedelta( + date_given = timezone.now() + expires_at = date_given + timedelta( days=settings.get('OIDC_SKIP_CONSENT_EXPIRE')) uc, created = UserConsent.objects.get_or_create( user=self.request.user, client=self.client, - defaults={'expires_at': expires_at}) + defaults={ + 'expires_at': expires_at, + 'date_given': date_given, + } + ) uc.scope = self.params.scope - # Rewrite expires_at if object already exists. + # Rewrite expires_at and date_given if object already exists. if not created: uc.expires_at = expires_at + uc.date_given = date_given uc.save() diff --git a/oidc_provider/migrations/0016_userconsent_and_verbosenames.py b/oidc_provider/migrations/0016_userconsent_and_verbosenames.py new file mode 100644 index 0000000..afd043e --- /dev/null +++ b/oidc_provider/migrations/0016_userconsent_and_verbosenames.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.6 on 2016-06-10 17:53 +from __future__ import unicode_literals + +import datetime +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('oidc_provider', '0015_change_client_code'), + ] + + operations = [ + migrations.AddField( + model_name='userconsent', + name='date_given', + field=models.DateTimeField(default=datetime.datetime(2016, 6, 10, 17, 53, 48, 889808, tzinfo=utc), verbose_name='Date Given'), + preserve_default=False, + ), + migrations.AlterField( + model_name='client', + name='_redirect_uris', + field=models.TextField(default=b'', help_text='Enter each URI on a new line.', verbose_name='Redirect URIs'), + ), + migrations.AlterField( + model_name='client', + name='client_id', + field=models.CharField(max_length=255, unique=True, verbose_name='Client ID'), + ), + migrations.AlterField( + model_name='client', + name='client_secret', + field=models.CharField(blank=True, default=b'', max_length=255, verbose_name='Client SECRET'), + ), + migrations.AlterField( + 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, verbose_name='Client Type'), + ), + migrations.AlterField( + model_name='client', + name='date_created', + field=models.DateField(auto_now_add=True, verbose_name='Date Created'), + ), + migrations.AlterField( + model_name='client', + name='name', + field=models.CharField(default=b'', max_length=100, verbose_name='Name'), + ), + migrations.AlterField( + model_name='client', + name='response_type', + field=models.CharField(choices=[(b'code', b'code (Authorization Code Flow)'), (b'id_token', b'id_token (Implicit Flow)'), (b'id_token token', b'id_token token (Implicit Flow)')], max_length=30, verbose_name='Response Type'), + ), + migrations.AlterField( + model_name='code', + name='_scope', + field=models.TextField(default=b'', verbose_name='Scopes'), + ), + migrations.AlterField( + model_name='code', + name='client', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='oidc_provider.Client', verbose_name='Client'), + ), + migrations.AlterField( + model_name='code', + name='code', + field=models.CharField(max_length=255, unique=True, verbose_name='Code'), + ), + migrations.AlterField( + model_name='code', + name='code_challenge', + field=models.CharField(max_length=255, null=True, verbose_name='Code Challenge'), + ), + migrations.AlterField( + model_name='code', + name='code_challenge_method', + field=models.CharField(max_length=255, null=True, verbose_name='Code Challenge Method'), + ), + migrations.AlterField( + model_name='code', + name='expires_at', + field=models.DateTimeField(verbose_name='Expiration Date'), + ), + migrations.AlterField( + model_name='code', + name='is_authentication', + field=models.BooleanField(default=False, verbose_name='Is Authentication?'), + ), + migrations.AlterField( + model_name='code', + name='nonce', + field=models.CharField(blank=True, default=b'', max_length=255, verbose_name='Nonce'), + ), + migrations.AlterField( + model_name='code', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'), + ), + migrations.AlterField( + model_name='rsakey', + name='key', + field=models.TextField(help_text='Paste your private RSA Key here.', verbose_name='Key'), + ), + migrations.AlterField( + model_name='token', + name='_id_token', + field=models.TextField(verbose_name='ID Token'), + ), + migrations.AlterField( + model_name='token', + name='_scope', + field=models.TextField(default=b'', verbose_name='Scopes'), + ), + migrations.AlterField( + model_name='token', + name='access_token', + field=models.CharField(max_length=255, unique=True, verbose_name='Access Token'), + ), + migrations.AlterField( + model_name='token', + name='client', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='oidc_provider.Client', verbose_name='Client'), + ), + migrations.AlterField( + model_name='token', + name='expires_at', + field=models.DateTimeField(verbose_name='Expiration Date'), + ), + migrations.AlterField( + model_name='token', + name='refresh_token', + field=models.CharField(max_length=255, null=True, unique=True, verbose_name='Refresh Token'), + ), + migrations.AlterField( + model_name='token', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'), + ), + migrations.AlterField( + model_name='userconsent', + name='_scope', + field=models.TextField(default=b'', verbose_name='Scopes'), + ), + migrations.AlterField( + model_name='userconsent', + name='client', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='oidc_provider.Client', verbose_name='Client'), + ), + migrations.AlterField( + model_name='userconsent', + name='expires_at', + field=models.DateTimeField(verbose_name='Expiration Date'), + ), + migrations.AlterField( + model_name='userconsent', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'), + ), + ] diff --git a/oidc_provider/models.py b/oidc_provider/models.py index 4c280e3..7495044 100644 --- a/oidc_provider/models.py +++ b/oidc_provider/models.py @@ -26,15 +26,15 @@ JWT_ALGS = [ 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, blank=True, default='') - response_type = models.CharField(max_length=30, choices=RESPONSE_TYPE_CHOICES) + name = models.CharField(max_length=100, default='', verbose_name=_(u'Name')) + client_type = models.CharField(max_length=30, choices=CLIENT_TYPE_CHOICES, default='confidential', verbose_name=_(u'Client Type'), 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, verbose_name=_(u'Client ID')) + client_secret = models.CharField(max_length=255, blank=True, default='', verbose_name=_(u'Client SECRET')) + response_type = models.CharField(max_length=30, choices=RESPONSE_TYPE_CHOICES, verbose_name=_(u'Response Type')) jwt_alg = models.CharField(max_length=10, choices=JWT_ALGS, default='RS256', verbose_name=_(u'JWT Algorithm')) - date_created = models.DateField(auto_now_add=True) + date_created = models.DateField(auto_now_add=True, verbose_name=_(u'Date Created')) - _redirect_uris = models.TextField(default='', verbose_name=_(u'Redirect URI'), help_text=_(u'Enter each URI on a new line.')) + _redirect_uris = models.TextField(default='', verbose_name=_(u'Redirect URIs'), help_text=_(u'Enter each URI on a new line.')) class Meta: verbose_name = _(u'Client') @@ -45,7 +45,7 @@ class Client(models.Model): def __unicode__(self): return self.__str__() - + def redirect_uris(): def fget(self): return self._redirect_uris.splitlines() @@ -61,10 +61,10 @@ class Client(models.Model): class BaseCodeTokenModel(models.Model): - user = models.ForeignKey(settings.AUTH_USER_MODEL) - client = models.ForeignKey(Client) - expires_at = models.DateTimeField() - _scope = models.TextField(default='') + user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_(u'User')) + client = models.ForeignKey(Client, verbose_name=_(u'Client')) + expires_at = models.DateTimeField(verbose_name=_(u'Expiration Date')) + _scope = models.TextField(default='', verbose_name=_(u'Scopes')) def scope(): def fget(self): @@ -82,18 +82,18 @@ class BaseCodeTokenModel(models.Model): def __unicode__(self): return self.__str__() - + class Meta: abstract = True class Code(BaseCodeTokenModel): - code = models.CharField(max_length=255, unique=True) - nonce = models.CharField(max_length=255, blank=True, default='') - is_authentication = models.BooleanField(default=False) - code_challenge = models.CharField(max_length=255, null=True) - code_challenge_method = models.CharField(max_length=255, null=True) + code = models.CharField(max_length=255, unique=True, verbose_name=_(u'Code')) + nonce = models.CharField(max_length=255, blank=True, default='', verbose_name=_(u'Nonce')) + is_authentication = models.BooleanField(default=False, verbose_name=_(u'Is Authentication?')) + code_challenge = models.CharField(max_length=255, null=True, verbose_name=_(u'Code Challenge')) + code_challenge_method = models.CharField(max_length=255, null=True, verbose_name=_(u'Code Challenge Method')) class Meta: verbose_name = _(u'Authorization Code') @@ -102,9 +102,9 @@ class Code(BaseCodeTokenModel): class Token(BaseCodeTokenModel): - access_token = models.CharField(max_length=255, unique=True) - refresh_token = models.CharField(max_length=255, unique=True, null=True) - _id_token = models.TextField() + access_token = models.CharField(max_length=255, unique=True, verbose_name=_(u'Access Token')) + refresh_token = models.CharField(max_length=255, unique=True, null=True, verbose_name=_(u'Refresh Token')) + _id_token = models.TextField(verbose_name=_(u'ID Token')) def id_token(): def fget(self): return json.loads(self._id_token) @@ -120,13 +120,15 @@ class Token(BaseCodeTokenModel): class UserConsent(BaseCodeTokenModel): + date_given = models.DateTimeField(verbose_name=_(u'Date Given')) + class Meta: unique_together = ('user', 'client') class RSAKey(models.Model): - key = models.TextField(help_text=_(u'Paste your private RSA Key here.')) + key = models.TextField(verbose_name=_(u'Key'), help_text=_(u'Paste your private RSA Key here.')) class Meta: verbose_name = _(u'RSA Key') diff --git a/oidc_provider/tests/app/settings.py b/oidc_provider/tests/app/settings.py index ef5f92b..281b28c 100644 --- a/oidc_provider/tests/app/settings.py +++ b/oidc_provider/tests/app/settings.py @@ -53,6 +53,8 @@ TEMPLATE_DIRS = [ 'oidc_provider/tests/templates', ] +USE_TZ = True + # OIDC Provider settings. SITE_URL = 'http://localhost:8000'