0002-misc-use-email-domain-validation-40200.patch
src/authentic2/forms/fields.py | ||
---|---|---|
17 | 17 |
import warnings |
18 | 18 |
import io |
19 | 19 | |
20 |
from django.forms import CharField, FileField, ValidationError |
|
20 |
from django.forms import CharField, FileField, ValidationError, EmailField
|
|
21 | 21 |
from django.forms.fields import FILE_INPUT_CONTRADICTION |
22 | 22 |
from django.utils.translation import ugettext_lazy as _ |
23 | 23 |
from django.core.files import File |
... | ... | |
26 | 26 |
from authentic2.passwords import password_help_text, validate_password |
27 | 27 |
from authentic2.forms.widgets import (PasswordInput, NewPasswordInput, |
28 | 28 |
CheckPasswordInput, ProfileImageInput) |
29 |
from authentic2.validators import email_validator |
|
29 | 30 | |
30 | 31 |
import PIL.Image |
31 | 32 | |
... | ... | |
111 | 112 | |
112 | 113 |
image = image.crop(box) |
113 | 114 |
return image.resize([width, height], PIL.Image.ANTIALIAS) |
115 | ||
116 | ||
117 |
class ValidatedEmailField(EmailField): |
|
118 |
def validate(self, value): |
|
119 |
super(ValidatedEmailField, self).validate(value) |
|
120 |
email_validator(value) |
src/authentic2/forms/passwords.py | ||
---|---|---|
25 | 25 | |
26 | 26 |
from .. import models, hooks, app_settings, utils |
27 | 27 |
from ..backends import get_user_queryset |
28 |
from .fields import PasswordField, NewPasswordField, CheckPasswordField |
|
28 |
from .fields import PasswordField, NewPasswordField, CheckPasswordField, ValidatedEmailField
|
|
29 | 29 |
from .utils import NextUrlFormMixin |
30 | 30 | |
31 | 31 | |
... | ... | |
35 | 35 |
class PasswordResetForm(forms.Form): |
36 | 36 |
next_url = forms.CharField(widget=forms.HiddenInput, required=False) |
37 | 37 | |
38 |
email = forms.EmailField(
|
|
38 |
email = ValidatedEmailField(
|
|
39 | 39 |
label=_("Email"), max_length=254) |
40 | 40 | |
41 | 41 |
def save(self): |
src/authentic2/forms/profile.py | ||
---|---|---|
20 | 20 |
from django import forms |
21 | 21 |
from django.utils.translation import ugettext_lazy as _, ugettext |
22 | 22 | |
23 |
from ..custom_user.models import User |
|
24 |
from .. import app_settings, models |
|
23 |
from authentic2 import app_settings, models |
|
24 |
from authentic2.custom_user.models import User |
|
25 |
from authentic2.validators import email_validator |
|
25 | 26 |
from .utils import NextUrlFormMixin |
26 | 27 |
from .mixins import LockedFieldFormMixin |
28 |
from .fields import ValidatedEmailField |
|
27 | 29 | |
28 | 30 | |
29 | 31 |
class DeleteAccountForm(forms.Form): |
... | ... | |
41 | 43 | |
42 | 44 | |
43 | 45 |
class EmailChangeFormNoPassword(forms.Form): |
44 |
email = forms.EmailField(label=_('New email'))
|
|
46 |
email = ValidatedEmailField(label=_('New email'))
|
|
45 | 47 | |
46 | 48 |
def __init__(self, user, *args, **kwargs): |
47 | 49 |
self.user = user |
... | ... | |
122 | 124 |
self.save_m2m = save_m2m |
123 | 125 |
return result |
124 | 126 | |
127 |
def clean_email(self): |
|
128 |
email = self.cleaned_data['email'] |
|
129 |
email_validator(email) |
|
130 |
return email |
|
131 | ||
125 | 132 | |
126 | 133 |
class EditProfileForm(NextUrlFormMixin, BaseUserForm): |
127 | 134 |
pass |
src/authentic2/forms/registration.py | ||
---|---|---|
19 | 19 |
from django.contrib.auth import get_user_model |
20 | 20 |
from django.core.exceptions import ValidationError |
21 | 21 |
from django.utils.translation import ugettext_lazy as _, ugettext |
22 |
from django.forms import Form, EmailField
|
|
22 |
from django.forms import Form |
|
23 | 23 | |
24 | 24 |
from django.contrib.auth.models import BaseUserManager, Group |
25 | 25 | |
... | ... | |
28 | 28 | |
29 | 29 |
from .. import app_settings, models |
30 | 30 |
from . import profile as profile_forms |
31 |
from .fields import ValidatedEmailField |
|
31 | 32 | |
32 | 33 |
User = get_user_model() |
33 | 34 | |
... | ... | |
36 | 37 |
error_css_class = 'form-field-error' |
37 | 38 |
required_css_class = 'form-field-required' |
38 | 39 | |
39 |
email = EmailField(label=_('Email')) |
|
40 |
email = ValidatedEmailField(label=_('Email'))
|
|
40 | 41 | |
41 | 42 |
def __init__(self, *args, **kwargs): |
42 | 43 |
super(RegistrationForm, self).__init__(*args, **kwargs) |
src/authentic2/manager/forms.py | ||
---|---|---|
29 | 29 | |
30 | 30 |
from authentic2.passwords import generate_password |
31 | 31 |
from authentic2.utils import send_templated_mail |
32 |
from authentic2.forms.fields import NewPasswordField, CheckPasswordField |
|
32 |
from authentic2.forms.fields import NewPasswordField, CheckPasswordField, ValidatedEmailField
|
|
33 | 33 | |
34 | 34 |
from django_rbac.models import Operation |
35 | 35 |
from django_rbac.utils import get_ou_model, get_role_model, get_permission_model |
... | ... | |
678 | 678 |
# we need a model form so that we can use a BaseEditView, a simple Form |
679 | 679 |
# would not work |
680 | 680 |
class UserChangeEmailForm(CssClass, FormWithRequest, forms.ModelForm): |
681 |
new_email = forms.EmailField(label=_('Email'))
|
|
681 |
new_email = ValidatedEmailField(label=_('Email'))
|
|
682 | 682 | |
683 | 683 |
def __init__(self, *args, **kwargs): |
684 | 684 |
initial = kwargs.setdefault('initial', {}) |
tests/settings.py | ||
---|---|---|
42 | 42 |
ALLOWED_HOSTS = ALLOWED_HOSTS + ['example.net', 'cache1.example.com', 'cache2.example.com'] |
43 | 43 | |
44 | 44 |
A2_AUTH_KERBEROS_ENABLED = False |
45 | ||
46 |
A2_VALIDATE_EMAIL_DOMAIN = False |
tests/test_manager.py | ||
---|---|---|
24 | 24 |
from webtest import Upload |
25 | 25 | |
26 | 26 |
from authentic2.a2_rbac.utils import get_default_ou |
27 |
from authentic2.validators import EmailValidator |
|
27 | 28 | |
28 | 29 |
from django_rbac.utils import get_ou_model, get_role_model |
29 | 30 |
from django.contrib.auth import get_user_model |
... | ... | |
293 | 294 |
assert urlparse(resp['Location']).path == url2 |
294 | 295 | |
295 | 296 | |
297 |
def test_manager_create_user_email_validation(superuser_or_admin, app, settings, monkeypatch): |
|
298 |
settings.A2_VALIDATE_EMAIL_DOMAIN = True |
|
299 |
monkeypatch.setattr(EmailValidator, 'check_mxs', lambda x, y: []) |
|
300 |
ou1 = OU.objects.create(name='OU1', slug='ou1') |
|
301 | ||
302 |
url = reverse('a2-manager-user-add', kwargs={'ou_pk': ou1.pk}) |
|
303 |
resp = login(app, superuser_or_admin, url) |
|
304 |
resp.form.set('first_name', 'John') |
|
305 |
resp.form.set('last_name', 'Doe') |
|
306 |
resp.form.set('email', 'john.doe@entrouvert.com') |
|
307 |
resp.form.set('password1', 'ABcd1234') |
|
308 |
resp.form.set('password2', 'ABcd1234') |
|
309 |
resp = resp.form.submit() |
|
310 |
assert 'domain is invalid' in resp.text |
|
311 | ||
312 |
monkeypatch.setattr(EmailValidator, 'check_mxs', lambda x, y: ['mx1.entrouvert.org']) |
|
313 |
resp.form.submit() |
|
314 |
assert User.objects.filter(email='john.doe@entrouvert.com').count() == 1 |
|
315 | ||
316 | ||
296 | 317 |
def test_app_setting_login_url(app, settings): |
297 | 318 |
settings.A2_MANAGER_LOGIN_URL = '/other_login/' |
298 | 319 |
response = app.get('/manage/') |
tests/test_registration.py | ||
---|---|---|
23 | 23 |
from django.utils.six.moves.urllib.parse import urlparse |
24 | 24 | |
25 | 25 |
from authentic2 import utils, models |
26 |
from authentic2.validators import EmailValidator |
|
26 | 27 | |
27 |
from utils import can_resolve_dns, get_link_from_mail
|
|
28 |
from utils import get_link_from_mail |
|
28 | 29 | |
29 | 30 | |
30 | 31 |
def test_registration(app, db, settings, mailoutbox, external_redirect): |
31 | 32 |
next_url, good_next_url = external_redirect |
32 | 33 | |
33 | 34 |
settings.LANGUAGE_CODE = 'en-us' |
34 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
35 | 35 |
settings.DEFAULT_FROM_EMAIL = 'show only addr <noreply@example.net>' |
36 | 36 | |
37 | 37 |
# disable existing attributes |
... | ... | |
102 | 102 | |
103 | 103 |
def test_registration_realm(app, db, settings, mailoutbox): |
104 | 104 |
settings.LANGUAGE_CODE = 'en-us' |
105 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
106 | 105 |
settings.A2_REGISTRATION_REALM = 'realm' |
107 | 106 |
settings.A2_REDIRECT_WHITELIST = ['http://relying-party.org/'] |
108 | 107 |
settings.A2_REQUIRED_FIELDS = ['username'] |
... | ... | |
156 | 155 |
assert urlparse(response['Location']).path == reverse('auth_homepage') |
157 | 156 | |
158 | 157 | |
158 |
def test_registration_email_validation(app, db, monkeypatch, settings): |
|
159 |
settings.A2_VALIDATE_EMAIL_DOMAIN = True |
|
160 |
monkeypatch.setattr(EmailValidator, 'check_mxs', lambda x, y: ['mx1.entrouvert.org']) |
|
161 | ||
162 |
resp = app.get(reverse('registration_register')) |
|
163 |
resp.form.set('email', 'testbot@entrouvert.com') |
|
164 |
resp = resp.form.submit().follow() |
|
165 |
assert 'Follow the instructions' in resp.text |
|
166 | ||
167 |
monkeypatch.setattr(EmailValidator, 'check_mxs', lambda x, y: []) |
|
168 |
resp = app.get(reverse('registration_register')) |
|
169 |
resp.form.set('email', 'testbot@entrouvert.com') |
|
170 |
resp = resp.form.submit() |
|
171 |
assert 'domain is invalid' in resp.text |
|
172 | ||
173 | ||
159 | 174 |
def test_username_settings(app, db, settings, mailoutbox): |
160 | 175 |
settings.LANGUAGE_CODE = 'en-us' |
161 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
162 | 176 |
settings.A2_REGISTRATION_FORM_USERNAME_REGEX = r'^(ab)+$' |
163 | 177 |
settings.A2_REGISTRATION_FORM_USERNAME_LABEL = 'Identifiant' |
164 | 178 |
settings.A2_REGISTRATION_FORM_USERNAME_HELP_TEXT = 'Bien remplir' |
... | ... | |
213 | 227 | |
214 | 228 |
def test_username_is_unique(app, db, settings, mailoutbox): |
215 | 229 |
settings.LANGUAGE_CODE = 'en-us' |
216 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
217 | 230 |
settings.A2_REGISTRATION_FIELDS = ['username'] |
218 | 231 |
settings.A2_REQUIRED_FIELDS = ['username'] |
219 | 232 |
settings.A2_USERNAME_IS_UNIQUE = True |
... | ... | |
261 | 274 | |
262 | 275 |
def test_email_is_unique(app, db, settings, mailoutbox): |
263 | 276 |
settings.LANGUAGE_CODE = 'en-us' |
264 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
265 | 277 |
settings.A2_EMAIL_IS_UNIQUE = True |
266 | 278 | |
267 | 279 |
# disable existing attributes |
... | ... | |
307 | 319 | |
308 | 320 |
def test_attribute_model(app, db, settings, mailoutbox): |
309 | 321 |
settings.LANGUAGE_CODE = 'en-us' |
310 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
311 | 322 |
# disable existing attributes |
312 | 323 |
models.Attribute.objects.update(disabled=True) |
313 | 324 | |
... | ... | |
400 | 411 | |
401 | 412 | |
402 | 413 |
def test_registration_bad_email(app, db, settings): |
403 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
404 | 414 |
settings.LANGUAGE_CODE = 'en-us' |
405 | 415 | |
406 | 416 |
response = app.post(reverse('registration_register'), params={'email': 'fred@0d..be'}, |
... | ... | |
455 | 465 | |
456 | 466 |
def test_revalidate_email(app, rf, db, settings, mailoutbox): |
457 | 467 |
settings.LANGUAGE_CODE = 'en-us' |
458 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
459 | 468 | |
460 | 469 |
# disable existing attributes |
461 | 470 |
models.Attribute.objects.update(disabled=True) |
... | ... | |
482 | 491 | |
483 | 492 |
def test_email_is_unique_multiple_objects_returned(app, db, settings, mailoutbox, rf): |
484 | 493 |
settings.LANGUAGE_CODE = 'en-us' |
485 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
486 | 494 |
settings.A2_REGISTRATION_EMAIL_IS_UNIQUE = True |
487 | 495 | |
488 | 496 |
# Create two user objects |
... | ... | |
506 | 514 | |
507 | 515 |
def test_username_is_unique_multiple_objects_returned(app, db, settings, mailoutbox, rf): |
508 | 516 |
settings.LANGUAGE_CODE = 'en-us' |
509 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
510 | 517 |
settings.A2_REGISTRATION_USERNAME_IS_UNIQUE = True |
511 | 518 |
settings.A2_REQUIRED_FIELDS = ['username', 'first_name', 'last_name'] |
512 | 519 | |
... | ... | |
535 | 542 | |
536 | 543 |
settings.A2_REGISTRATION_REDIRECT = 'http://cms/welcome/' |
537 | 544 |
settings.LANGUAGE_CODE = 'en-us' |
538 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
539 | 545 | |
540 | 546 |
new_next_url = settings.A2_REGISTRATION_REDIRECT |
541 | 547 |
if good_next_url: |
... | ... | |
590 | 596 |
next_url, good_next_url = external_redirect |
591 | 597 | |
592 | 598 |
settings.A2_REGISTRATION_REDIRECT = 'http://cms/welcome/', 'target' |
593 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
594 | 599 | |
595 | 600 |
new_next_url = settings.A2_REGISTRATION_REDIRECT[0] |
596 | 601 |
if good_next_url: |
... | ... | |
614 | 619 | |
615 | 620 |
def test_registration_activate_passwords_not_equal(app, db, settings, mailoutbox): |
616 | 621 |
settings.LANGUAGE_CODE = 'en-us' |
617 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
618 | 622 |
settings.A2_EMAIL_IS_UNIQUE = True |
619 | 623 | |
620 | 624 |
response = app.get(reverse('registration_register')) |
tests/utils.py | ||
---|---|---|
162 | 162 |
'%r not found in log records' % message |
163 | 163 | |
164 | 164 | |
165 |
def can_resolve_dns(): |
|
166 |
'''Verify that DNS resolving is available''' |
|
167 |
import socket |
|
168 |
try: |
|
169 |
return isinstance(socket.gethostbyname('entrouvert.com'), str) |
|
170 |
except: |
|
171 |
return False |
|
172 | ||
173 | ||
174 | 165 |
def get_links_from_mail(mail): |
175 | 166 |
'''Extract links from mail sent by Django''' |
176 | 167 |
return re.findall('https?://[^ \n]*', mail.body) |
177 |
- |