From 253855f22ecd856c7f4142dfe4d699d40ce680f2 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Fri, 3 May 2019 11:37:34 +0200 Subject: [PATCH 4/4] views: user enabling of authentication factors (#33550) A supplementary authentication factor needs to be set up by the user before they can use it. Either they will do it proactively before needing it, or they will stumble upon a page needing an higher authentication level. This commit allows for both. --- .../migrations/0025_auto_20190502_1558.py | 35 +++++++++++++++++++ src/authentic2/models.py | 10 ++++++ src/authentic2/views.py | 16 +++++++-- 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/authentic2/migrations/0025_auto_20190502_1558.py diff --git a/src/authentic2/migrations/0025_auto_20190502_1558.py b/src/authentic2/migrations/0025_auto_20190502_1558.py new file mode 100644 index 00000000..a726a2e9 --- /dev/null +++ b/src/authentic2/migrations/0025_auto_20190502_1558.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.18 on 2019-05-02 13:58 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('custom_user', '0016_auto_20180925_1107'), + ('authentic2', '0023_auto_20181031_0900'), + ] + + operations = [ + migrations.CreateModel( + name='EnabledAuthFactor', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('authenticator_id', models.CharField(max_length=50)), + ], + ), + migrations.AddField( + model_name='enabledauthfactor', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='enabled_auth_factors', to=settings.AUTH_USER_MODEL, verbose_name='user'), + ), + migrations.AlterUniqueTogether( + name='enabledauthfactor', + unique_together=set([('user', 'authenticator_id')]), + ), + ] diff --git a/src/authentic2/models.py b/src/authentic2/models.py index d22e3a6b..e7a86f81 100644 --- a/src/authentic2/models.py +++ b/src/authentic2/models.py @@ -77,6 +77,16 @@ class UserExternalId(models.Model): verbose_name_plural = _('user external ids') +class EnabledAuthFactor(models.Model): + user = models.ForeignKey(settings.AUTH_USER_MODEL, + verbose_name=_('user'), + related_name='enabled_auth_factors') + authenticator_id = models.CharField(max_length=50) + + class Meta: + unique_together = ('user', 'authenticator_id') + + @six.python_2_unicode_compatible class AuthenticationEvent(models.Model): '''Record authentication events whatever the source''' diff --git a/src/authentic2/views.py b/src/authentic2/views.py index 3fa4ae0b..5dab3a16 100644 --- a/src/authentic2/views.py +++ b/src/authentic2/views.py @@ -300,6 +300,17 @@ def login(request, template_name='authentic2/login.html', authenticators = utils.get_backends('AUTH_FRONTENDS', target_auth_level) + if target_auth_level > 1: + # Filter authenticators enabled by the user + authenticator_ids = set(request.user.enabled_auth_factors.values_list( + 'authenticator_id', flat=True)) + authenticators = [a for a in authenticators if a.id in authenticator_ids] + if not authenticators: + messages.info(request, _('In order to continue you need to setup ' + 'a new authentication factor.')) + return utils.redirect(request, 'account_management', + keep_params=True) + blocks = [] registration_url = utils.get_registration_url( @@ -418,9 +429,9 @@ class ProfileView(cbv.TemplateNamesMixin, TemplateView): def get_context_data(self, **kwargs): context = super(ProfileView, self).get_context_data(**kwargs) - frontends = utils.get_backends('AUTH_FRONTENDS', required_auth_level=0) - request = self.request + auth_level = int(request.GET.get('auth_level', 0)) + frontends = utils.get_backends('AUTH_FRONTENDS', required_auth_level=auth_level) if request.method == "POST": for frontend in frontends: @@ -518,6 +529,7 @@ class ProfileView(cbv.TemplateNamesMixin, TemplateView): # TODO: deprecated should be removed when publik-base-theme is updated 'allow_password_change': utils.user_can_change_password(request=request), 'federation_management': federation_management, + 'auth_level': auth_level, }) hooks.call_hooks('modify_context_data', self, context) return context -- 2.20.1