Project

General

Profile

0002-auth_fc-add-flag-to-disable-link-by-email-68360.patch

Benjamin Dauvergne, 23 November 2022 02:58 PM

Download (6.75 KB)

View differences:

Subject: [PATCH 2/2] auth_fc: add flag to disable link by email (#68360)

 .../0008_fcauthenticator_link_by_email.py     | 18 +++++++
 src/authentic2_auth_fc/models.py              | 10 +++-
 src/authentic2_auth_fc/views.py               | 12 +++--
 tests/auth_fc/conftest.py                     |  7 ++-
 tests/auth_fc/test_auth_fc.py                 | 49 ++++++++++++++-----
 5 files changed, 77 insertions(+), 19 deletions(-)
 create mode 100644 src/authentic2_auth_fc/migrations/0008_fcauthenticator_link_by_email.py
src/authentic2_auth_fc/migrations/0008_fcauthenticator_link_by_email.py
1
# Generated by Django 3.2.16 on 2022-11-23 13:38
2

  
3
from django.db import migrations, models
4

  
5

  
6
class Migration(migrations.Migration):
7

  
8
    dependencies = [
9
        ('authentic2_auth_fc', '0007_auto_20220615_1002'),
10
    ]
11

  
12
    operations = [
13
        migrations.AddField(
14
            model_name='fcauthenticator',
15
            name='link_by_email',
16
            field=models.BooleanField(default=True, verbose_name='Link by email address'),
17
        ),
18
    ]
src/authentic2_auth_fc/models.py
68 68
        verbose_name=_('Scopes'),
69 69
        default=get_default_scopes,
70 70
    )
71
    link_by_email = models.BooleanField(_('Link by email address'), default=True)
71 72

  
72 73
    type = 'fc'
73 74
    how = ['france-connect']
74 75
    unique = True
75
    description_fields = ['show_condition', 'platform', 'client_id', 'client_secret', 'scopes']
76
    description_fields = [
77
        'show_condition',
78
        'platform',
79
        'client_id',
80
        'client_secret',
81
        'scopes',
82
        'link_by_email',
83
    ]
76 84

  
77 85
    class Meta:
78 86
        verbose_name = _('FranceConnect')
src/authentic2_auth_fc/views.py
60 60
User = get_user_model()
61 61

  
62 62

  
63
class UserOutsideDefaultOu(Exception):
63
class EmailExistsError(Exception):
64 64
    pass
65 65

  
66 66

  
......
429 429
            # try to create or find an user with this email
430 430
            try:
431 431
                user, created = self.get_or_create_user_with_email(email)
432
            except UserOutsideDefaultOu:
433
                user = None
434
            except User.MultipleObjectsReturned:
432
            except EmailExistsError:
435 433
                user = None
436 434
            if not user:
437 435
                messages.warning(
......
549 547
        Lock.lock_email(email)
550 548
        try:
551 549
            user = qs.get_by_email(email)
550
            if not self.authenticator.link_by_email:
551
                raise EmailExistsError
552 552
        except User.DoesNotExist:
553 553
            return User.objects.create(ou=ou, email=email), True
554
        except User.MultipleObjectsReturned:
555
            raise EmailExistsError
554 556

  
555 557
        if user.ou != ou:
556
            raise UserOutsideDefaultOu
558
            raise EmailExistsError
557 559
        return user, False
558 560

  
559 561

  
tests/auth_fc/conftest.py
166 166

  
167 167

  
168 168
@pytest.fixture
169
def franceconnect(settings, service, db):
170
    FcAuthenticator.objects.create(
169
def authenticator(db):
170
    return FcAuthenticator.objects.create(
171 171
        enabled=True,
172 172
        client_id=CLIENT_ID,
173 173
        client_secret=CLIENT_SECRET,
......
175 175
        scopes=['profile', 'email'],
176 176
    )
177 177

  
178

  
179
@pytest.fixture
180
def franceconnect(settings, authenticator, service, db):
178 181
    mock_object = FranceConnectMock()
179 182
    with mock_object():
180 183
        yield mock_object
tests/auth_fc/test_auth_fc.py
192 192
    assert User.objects.count() == 0
193 193

  
194 194

  
195
def test_login_email_is_unique(settings, app, franceconnect, caplog):
196
    settings.A2_EMAIL_IS_UNIQUE = True
197
    user = User(email='john.doe@example.com', first_name='John', last_name='Doe', ou=get_default_ou())
198
    user.set_password('toto')
199
    user.save()
200
    franceconnect.user_info['email'] = user.email
201

  
202
    assert User.objects.count() == 1
203
    franceconnect.login_with_fc_fixed_params(app)
204
    assert User.objects.count() == 1
205
    assert app.session['_auth_user_id'] == str(user.pk)
195
class TestLinkByEmail:
196
    @pytest.fixture
197
    def franceconnect(self, franceconnect):
198
        franceconnect.callback_params = {'next': '/accounts/'}
199
        return franceconnect
200

  
201
    def test_enabled(self, settings, app, franceconnect, authenticator, caplog):
202
        authenticator.link_by_email = True
203
        authenticator.save()
204

  
205
        user = User(email='john.doe@example.com', first_name='John', last_name='Doe', ou=get_default_ou())
206
        user.set_password('toto')
207
        user.save()
208
        franceconnect.user_info['email'] = user.email
209

  
210
        assert User.objects.count() == 1
211
        franceconnect.login_with_fc_fixed_params(app)
212
        assert User.objects.count() == 1
213
        assert '_auth_user_id' in app.session
214

  
215
    def test_disabled(self, settings, app, franceconnect, authenticator, caplog):
216
        authenticator.link_by_email = False
217
        authenticator.save()
218

  
219
        user = User(email='john.doe@example.com', first_name='John', last_name='Doe', ou=get_default_ou())
220
        user.set_password('toto')
221
        user.save()
222
        franceconnect.user_info['email'] = user.email
223

  
224
        assert User.objects.count() == 1
225
        response = franceconnect.login_with_fc_fixed_params(app)
226
        assert User.objects.count() == 1
227
        assert '_auth_user_id' not in app.session
228

  
229
        # no login, so we must have produced a logout request toward FC
230
        response = franceconnect.handle_logout(app, response.location)
231
        response = response.maybe_follow()
232
        assert 'Your FranceConnect email address' in response.pyquery('.messages .warning').text()
206 233

  
207 234

  
208 235
def test_link_after_login_with_password(app, franceconnect, simple_user):
209
-