mirror of
https://github.com/GSA/notifications-api.git
synced 2026-02-04 02:11:11 -05:00
Merge pull request #2582 from alphagov/punycode
punycode encode emails before sending
This commit is contained in:
@@ -83,7 +83,7 @@ class AwsSesClient(EmailClient):
|
|||||||
response = self._client.send_email(
|
response = self._client.send_email(
|
||||||
Source=source,
|
Source=source,
|
||||||
Destination={
|
Destination={
|
||||||
'ToAddresses': to_addresses,
|
'ToAddresses': [punycode_encode_email(addr) for addr in to_addresses],
|
||||||
'CcAddresses': [],
|
'CcAddresses': [],
|
||||||
'BccAddresses': []
|
'BccAddresses': []
|
||||||
},
|
},
|
||||||
@@ -93,7 +93,7 @@ class AwsSesClient(EmailClient):
|
|||||||
},
|
},
|
||||||
'Body': body
|
'Body': body
|
||||||
},
|
},
|
||||||
ReplyToAddresses=reply_to_addresses
|
ReplyToAddresses=[punycode_encode_email(addr) for addr in reply_to_addresses]
|
||||||
)
|
)
|
||||||
except botocore.exceptions.ClientError as e:
|
except botocore.exceptions.ClientError as e:
|
||||||
self.statsd_client.incr("clients.ses.error")
|
self.statsd_client.incr("clients.ses.error")
|
||||||
@@ -116,3 +116,9 @@ class AwsSesClient(EmailClient):
|
|||||||
self.statsd_client.timing("clients.ses.request-time", elapsed_time)
|
self.statsd_client.timing("clients.ses.request-time", elapsed_time)
|
||||||
self.statsd_client.incr("clients.ses.success")
|
self.statsd_client.incr("clients.ses.success")
|
||||||
return response['MessageId']
|
return response['MessageId']
|
||||||
|
|
||||||
|
|
||||||
|
def punycode_encode_email(email_address):
|
||||||
|
# only the hostname should ever be punycode encoded.
|
||||||
|
local, hostname = email_address.split('@')
|
||||||
|
return '{}@{}'.format(local, hostname.encode('idna').decode('utf-8'))
|
||||||
|
|||||||
@@ -45,21 +45,21 @@ def test_should_be_none_if_unrecognised_status_code():
|
|||||||
assert '99' in str(e.value)
|
assert '99' in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize('reply_to_address, expected_value', [
|
||||||
'reply_to_address, expected_value',
|
(None, []),
|
||||||
[(None, []), ('foo@bar.com', ['foo@bar.com'])],
|
('foo@bar.com', ['foo@bar.com']),
|
||||||
ids=['empty', 'single_email']
|
('føøøø@bååååår.com', ['føøøø@xn--br-yiaaaaa.com'])
|
||||||
)
|
], ids=['empty', 'single_email', 'punycode'])
|
||||||
def test_send_email_handles_reply_to_address(notify_api, mocker, reply_to_address, expected_value):
|
def test_send_email_handles_reply_to_address(notify_api, mocker, reply_to_address, expected_value):
|
||||||
boto_mock = mocker.patch.object(aws_ses_client, '_client', create=True)
|
boto_mock = mocker.patch.object(aws_ses_client, '_client', create=True)
|
||||||
mocker.patch.object(aws_ses_client, 'statsd_client', create=True)
|
mocker.patch.object(aws_ses_client, 'statsd_client', create=True)
|
||||||
|
|
||||||
with notify_api.app_context():
|
with notify_api.app_context():
|
||||||
aws_ses_client.send_email(
|
aws_ses_client.send_email(
|
||||||
Mock(),
|
source=Mock(),
|
||||||
Mock(),
|
to_addresses='to@address.com',
|
||||||
Mock(),
|
subject=Mock(),
|
||||||
Mock(),
|
body=Mock(),
|
||||||
reply_to_address=reply_to_address
|
reply_to_address=reply_to_address
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -71,6 +71,26 @@ def test_send_email_handles_reply_to_address(notify_api, mocker, reply_to_addres
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_send_email_handles_punycode_to_address(notify_api, mocker):
|
||||||
|
boto_mock = mocker.patch.object(aws_ses_client, '_client', create=True)
|
||||||
|
mocker.patch.object(aws_ses_client, 'statsd_client', create=True)
|
||||||
|
|
||||||
|
with notify_api.app_context():
|
||||||
|
aws_ses_client.send_email(
|
||||||
|
Mock(),
|
||||||
|
to_addresses='føøøø@bååååår.com',
|
||||||
|
subject=Mock(),
|
||||||
|
body=Mock()
|
||||||
|
)
|
||||||
|
|
||||||
|
boto_mock.send_email.assert_called_once_with(
|
||||||
|
Source=ANY,
|
||||||
|
Destination={'ToAddresses': ['føøøø@xn--br-yiaaaaa.com'], 'CcAddresses': [], 'BccAddresses': []},
|
||||||
|
Message=ANY,
|
||||||
|
ReplyToAddresses=ANY
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_send_email_raises_bad_email_as_InvalidEmailError(mocker):
|
def test_send_email_raises_bad_email_as_InvalidEmailError(mocker):
|
||||||
boto_mock = mocker.patch.object(aws_ses_client, '_client', create=True)
|
boto_mock = mocker.patch.object(aws_ses_client, '_client', create=True)
|
||||||
mocker.patch.object(aws_ses_client, 'statsd_client', create=True)
|
mocker.patch.object(aws_ses_client, 'statsd_client', create=True)
|
||||||
@@ -87,13 +107,13 @@ def test_send_email_raises_bad_email_as_InvalidEmailError(mocker):
|
|||||||
with pytest.raises(InvalidEmailError) as excinfo:
|
with pytest.raises(InvalidEmailError) as excinfo:
|
||||||
aws_ses_client.send_email(
|
aws_ses_client.send_email(
|
||||||
source=Mock(),
|
source=Mock(),
|
||||||
to_addresses='clearly@invalid@email.com',
|
to_addresses='definitely@invalid_email.com',
|
||||||
subject=Mock(),
|
subject=Mock(),
|
||||||
body=Mock()
|
body=Mock()
|
||||||
)
|
)
|
||||||
|
|
||||||
assert 'some error message from amazon' in str(excinfo.value)
|
assert 'some error message from amazon' in str(excinfo.value)
|
||||||
assert 'clearly@invalid@email.com' in str(excinfo.value)
|
assert 'definitely@invalid_email.com' in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
def test_send_email_raises_other_errs_as_AwsSesClientException(mocker):
|
def test_send_email_raises_other_errs_as_AwsSesClientException(mocker):
|
||||||
@@ -112,7 +132,7 @@ def test_send_email_raises_other_errs_as_AwsSesClientException(mocker):
|
|||||||
with pytest.raises(AwsSesClientException) as excinfo:
|
with pytest.raises(AwsSesClientException) as excinfo:
|
||||||
aws_ses_client.send_email(
|
aws_ses_client.send_email(
|
||||||
source=Mock(),
|
source=Mock(),
|
||||||
to_addresses=Mock(),
|
to_addresses='foo@bar.com',
|
||||||
subject=Mock(),
|
subject=Mock(),
|
||||||
body=Mock()
|
body=Mock()
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user