Projet

Général

Profil

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

Thomas Noël, 12 décembre 2022 11:16

Télécharger (7,95 ko)

Voir les différences:

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

 hobo/emails/forms.py      |  3 ++-
 hobo/emails/validators.py | 18 ++++++++++++++
 hobo/settings.py          |  4 ++++
 tests/test_emails.py      | 49 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 72 insertions(+), 2 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):
......
25 25
        validate_email(value)
26 26
        validate_email_address(value)
27 27
        validate_email_spf(value)
28
        validate_email_domain(value)
28 29

  
29 30

  
30 31
class EmailsForm(forms.Form):
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
......
75 76
            if allowed_record in spf_record:
76 77
                return
77 78
    raise ValidationError(_('No suitable SPF record found for %s') % email_domain)
79

  
80

  
81
def validate_email_domain(value):
82
    domain = value.split('@')[-1].strip().strip('.')
83
    domains = settings.EMAIL_FROM_ACCEPTED_DOMAINS
84
    if '*' in domains or domain in domains:
85
        return
86
    known_services = getattr(settings, 'KNOWN_SERVICES', {})
87
    for app in known_services.values():
88
        for instance in app.values():
89
            url = instance.get('url')
90
            if not url:
91
                continue
92
            fqdn = urllib.parse.urlparse(url).netloc.split(':')[0]
93
            if fqdn == domain:
94
                return
95
    raise ValidationError(_('Domain %s is not accepted') % domain)
hobo/settings.py
29 29

  
30 30
HOBO_VALIDATE_EMAIL_WITH_SMTP = True
31 31
ALLOWED_SPF_RECORDS = []
32
# EMAIL_SENDER_ACCEPTED_DOMAINS: list of accepted domains for
33
# default_from_email. It can contains '*' to accept all domains.
34
# Note: all KNOWN_SERVICES url domains are always accepter
35
EMAIL_FROM_ACCEPTED_DOMAINS = []
32 36

  
33 37
# Application definition
34 38

  
tests/test_emails.py
144 144
    assert 'Email address not found' in force_text(response.content)
145 145

  
146 146

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

  
155 156

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

  
166 168

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

  
174 177

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

  
183 187

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

  
193 198

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

  
205 211

  
212
def test_sender_accepted_domains(client, admin_user, dns_resolver, smtp_server, settings):
213
    settings.HOBO_VALIDATE_EMAIL_WITH_SMTP = False
214

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

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

  
227
    settings.EMAIL_FROM_ACCEPTED_DOMAINS = []
228
    settings.KNOWN_SERVICES = {
229
        'combo': {
230
            'portal': {
231
                'title': 'Portal',
232
                'url': 'https://example.org/test',
233
            },
234
            'other': {
235
                'title': 'Empty',
236
            },
237
        }
238
    }
239
    response = client.post('/emails/', {'default_from_email': 'john.doe@example.org'}, follow=True)
240
    assert response.status_code == 200
241
    assert 'Emails settings have been updated.' in force_text(response.content)
242

  
243
    response = client.post('/emails/', {'default_from_email': 'john.doe@example.com'}, follow=True)
244
    assert response.status_code == 200
245
    assert 'Domain example.com is not accepted' in force_text(response.content)
246

  
247
    response = client.post('/emails/', {'default_from_email': 'john.doe@brother.example.org'}, follow=True)
248
    assert response.status_code == 200
249
    assert 'Domain brother.example.org is not accepted' 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_ACCEPTED_DOMAINS = ['*']
207 254
    settings.ALLOWED_SPF_RECORDS = ['include:allowed_mx.com']
208 255
    app = login(app)
209 256
    resp = app.get('/emails/')
210
-