0002-auth_fc-add-flag-to-disable-link-by-email-68360.patch
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 |
- |