Projet

Général

Profil

0001-emails-restrict-domains-for-default_from_email-72173.patch

Thomas Noël, 03 janvier 2023 14:33

Télécharger (8,92 ko)

Voir les différences:

Subject: [PATCH] emails: restrict domains for default_from_email (#72173)

 hobo/emails/forms.py      |  3 ++-
 hobo/emails/validators.py | 14 +++++++++++
 hobo/settings.py          |  4 +++
 tests/test_emails.py      | 53 ++++++++++++++++++++++++++++++++++++---
 4 files changed, 70 insertions(+), 4 deletions(-)
hobo/emails/forms.py
17 17
from django.core.validators import validate_email
18 18
from django.utils.translation import ugettext_lazy as _
19 19

  
20
from hobo.emails.validators import validate_email_address, validate_email_spf
20
from hobo.emails.validators import validate_email_address, validate_email_spf, validate_email_domain
21 21

  
22 22

  
23 23
class ValidEmailField(forms.EmailField):
24 24
    def validate(self, value):
25 25
        validate_email(value)
26
        validate_email_domain(value)
26 27
        validate_email_address(value)
27 28
        validate_email_spf(value)
28 29

  
hobo/emails/validators.py
16 16

  
17 17
import smtplib
18 18
import socket
19
import urllib.parse
19 20

  
20 21
import dns.resolver
21 22
from django.conf import settings
......
23 24
from django.utils.encoding import force_bytes
24 25
from django.utils.translation import ugettext_lazy as _
25 26

  
27
from hobo.environment.utils import get_operational_services
26 28

  
27 29
def validate_email_address(value):
28 30
    if not settings.HOBO_VALIDATE_EMAIL_WITH_SMTP:
......
75 77
            if allowed_record in spf_record:
76 78
                return
77 79
    raise ValidationError(_('No suitable SPF record found for %s') % email_domain)
80

  
81

  
82
def validate_email_domain(value):
83
    domain = value.split('@')[-1].strip().strip('.')
84
    domains = settings.EMAIL_FROM_ALLOWED_DOMAINS
85
    if '*' in domains or domain in domains:
86
        return
87
    for service in get_operational_services():
88
        fqdn = urllib.parse.urlparse(service.base_url).netloc.split(':')[0]
89
        if fqdn == domain or fqdn == 'www.' + domain:
90
            return
91
    raise ValidationError(_('Domain %s is not allowed') % domain)
hobo/settings.py
29 29

  
30 30
HOBO_VALIDATE_EMAIL_WITH_SMTP = True
31 31
ALLOWED_SPF_RECORDS = []
32
# EMAIL_FROM_ALLOWED_DOMAINS: list of allowed domains for default_from_email.
33
# Use ['*'] to allow all domains.
34
# Note: all KNOWN_SERVICES url domains are always allowed
35
EMAIL_FROM_ALLOWED_DOMAINS = []
32 36

  
33 37
# Application definition
34 38

  
tests/test_emails.py
15 15
from test_manager import login
16 16

  
17 17
from hobo.emails.validators import validate_email_address
18
from hobo.environment.models import Variable
18
from hobo.environment.models import Variable, ServiceBase, Combo, Wcs
19 19

  
20 20

  
21 21
@pytest.fixture
......
137 137
    assert 'Enter a valid email address' in force_text(response.content)
138 138

  
139 139

  
140
def test_unkown_address(client, admin_user, dns_resolver, smtp_server):
140
def test_unkown_address(client, admin_user, dns_resolver, smtp_server, settings):
141
    settings.EMAIL_FROM_ALLOWED_DOMAINS = ['*']
141 142
    client.post('/login/', {'username': 'admin', 'password': 'password'})
142 143
    response = client.post('/emails/', {'default_from_email': 'john.doe@unknown.com'})
143 144
    assert response.status_code == 200
144 145
    assert 'Email address not found' in force_text(response.content)
145 146

  
146 147

  
147
def test_kown_address_nospf(client, admin_user, dns_resolver, smtp_server):
148
def test_kown_address_nospf(client, admin_user, dns_resolver, smtp_server, settings):
149
    settings.EMAIL_FROM_ALLOWED_DOMAINS = ['*']
148 150
    client.post('/login/', {'username': 'admin', 'password': 'password'})
149 151
    response = client.post('/emails/', {'default_from_email': 'john.doe@example.com'}, follow=True)
150 152
    assert response.status_code == 200
......
154 156

  
155 157

  
156 158
def test_spf_allow_all_mail(client, admin_user, dns_resolver, smtp_server, settings):
159
    settings.EMAIL_FROM_ALLOWED_DOMAINS = ['*']
157 160
    client.post('/login/', {'username': 'admin', 'password': 'password'})
158 161
    response = client.post(
159 162
        '/emails/', {'default_from_email': 'john.doe@example-spf-allow-all.com'}, follow=True
......
165 168

  
166 169

  
167 170
def test_invalid_spf(client, admin_user, dns_resolver, smtp_server, settings):
171
    settings.EMAIL_FROM_ALLOWED_DOMAINS = ['*']
168 172
    settings.ALLOWED_SPF_RECORDS = ['include:example.com']
169 173
    client.post('/login/', {'username': 'admin', 'password': 'password'})
170 174
    response = client.post('/emails/', {'default_from_email': 'john.doe@example-invalid-spf.com'})
......
173 177

  
174 178

  
175 179
def test_strict_nospf(client, admin_user, dns_resolver, smtp_server, monkeypatch, settings):
180
    settings.EMAIL_FROM_ALLOWED_DOMAINS = ['*']
176 181
    settings.ALLOWED_SPF_RECORDS = ['include:allowed_mx.com']
177 182
    monkeypatch.setattr('hobo.emails.validators.validate_email_spf.__defaults__', (True,))
178 183
    client.post('/login/', {'username': 'admin', 'password': 'password'})
......
182 187

  
183 188

  
184 189
def test_valid_spf(client, admin_user, dns_resolver, smtp_server, settings):
190
    settings.EMAIL_FROM_ALLOWED_DOMAINS = ['*']
185 191
    settings.ALLOWED_SPF_RECORDS = ['include:allowed_mx.com']
186 192
    client.post('/login/', {'username': 'admin', 'password': 'password'})
187 193
    response = client.post('/emails/', {'default_from_email': 'john.doe@example-spf.com'}, follow=True)
......
192 198

  
193 199

  
194 200
def test_no_spf_validation(client, admin_user, dns_resolver, smtp_server, settings):
201
    settings.EMAIL_FROM_ALLOWED_DOMAINS = ['*']
195 202
    settings.ALLOWED_SPF_RECORDS = []
196 203
    client.post('/login/', {'username': 'admin', 'password': 'password'})
197 204
    response = client.post(
......
203 210
    )
204 211

  
205 212

  
213
def test_sender_allowed_domains(client, admin_user, dns_resolver, smtp_server, settings, monkeypatch):
214
    settings.HOBO_VALIDATE_EMAIL_WITH_SMTP = False
215

  
216
    client.post('/login/', {'username': 'admin', 'password': 'password'})
217
    response = client.post('/emails/', {'default_from_email': 'john.doe@example.com'}, follow=True)
218
    assert response.status_code == 200
219
    assert 'Domain example.com is not allowed' in force_text(response.content)
220
    assert 'Emails settings have been updated.' not in force_text(response.content)
221

  
222
    settings.EMAIL_FROM_ALLOWED_DOMAINS = ['example.com', 'foo.bar']
223
    response = client.post('/emails/', {'default_from_email': 'john.doe@example.com'}, follow=True)
224
    assert response.status_code == 200
225
    assert 'Emails settings have been updated.' in force_text(response.content)
226
    assert 'Domain example.com is not allowed' not in force_text(response.content)
227

  
228
    settings.EMAIL_FROM_ALLOWED_DOMAINS = []
229
    combo = Combo(base_url='https://example.org/test')
230
    combo.save()
231
    monkeypatch.setattr(ServiceBase, 'is_operational', lambda x: True)
232

  
233
    response = client.post('/emails/', {'default_from_email': 'john.doe@example.org'}, follow=True)
234
    assert response.status_code == 200
235
    assert 'Emails settings have been updated.' in force_text(response.content)
236

  
237
    response = client.post('/emails/', {'default_from_email': 'john.doe@example.com'}, follow=True)
238
    assert response.status_code == 200
239
    assert 'Domain example.com is not allowed' in force_text(response.content)
240

  
241
    response = client.post('/emails/', {'default_from_email': 'john.doe@brother.example.org'}, follow=True)
242
    assert response.status_code == 200
243
    assert 'Domain brother.example.org is not allowed' in force_text(response.content)
244

  
245
    combo.base_url = 'https://www.example.com'
246
    combo.save()
247
    response = client.post('/emails/', {'default_from_email': 'john.doe@example.com'}, follow=True)
248
    assert response.status_code == 200
249
    assert 'Emails settings have been updated.' in force_text(response.content)
250

  
251

  
206 252
def test_emails_view(app, admin_user, dns_resolver, smtp_server, settings):
253
    settings.EMAIL_FROM_ALLOWED_DOMAINS = ['*']
207 254
    settings.ALLOWED_SPF_RECORDS = ['include:allowed_mx.com']
208 255
    app = login(app)
209 256
    resp = app.get('/emails/')
210
-