Simplify socketlabs check for status and messaging when something goes wrong. (#22005)
* Simplify socketlabs check for status and messaging when something goes wrong. * Fix comments * Add more details and formatting to suppressed email status table. * Copy fixes and updates * Update src/olympia/devhub/templates/devhub/verify_email.html Co-authored-by: Francesco Lodolo <flodolo@mozilla.com> --------- Co-authored-by: Mozilla Add-ons Robot <addons-dev-automation+github@mozilla.com> Co-authored-by: Francesco Lodolo <flodolo@mozilla.com>
This commit is contained in:
Родитель
b9379559f3
Коммит
059193a3d3
|
@ -7,6 +7,7 @@
|
|||
{% block content %}
|
||||
<h1>{{ title }}</h1>
|
||||
<div id="{{ state }}">
|
||||
<div class="verify-email-text">
|
||||
{% if state == "email_verified" %}
|
||||
{% trans %}
|
||||
Your email address is verified.
|
||||
|
@ -20,24 +21,13 @@
|
|||
Could not verify email address. The verification link has expired.
|
||||
{% endtrans %}
|
||||
{% elif state == "verification_pending" %}
|
||||
{% trans %}
|
||||
Working... Please be patient.
|
||||
{% endtrans %}
|
||||
<div class="loader"></div>
|
||||
{% elif state == "verification_failed" %}
|
||||
{% trans %}
|
||||
Failed to send confirmation email. Please try again.
|
||||
If you no longer have access to your email address, please update your mozilla account email address.
|
||||
<a
|
||||
href="https://support.mozilla.org/en-US/kb/change-primary-email-address-firefox-accounts"
|
||||
target="_blank"
|
||||
>
|
||||
Change email address
|
||||
</a>
|
||||
{% endtrans %}
|
||||
{% trans %}
|
||||
We are sending an email to you, this might take a minute. The page will automatically refresh.
|
||||
{% endtrans %}
|
||||
{% elif state == "verification_timedout" %}
|
||||
{% trans %}
|
||||
This is taking longer than expected. Try again.
|
||||
It is taking longer than expected to confirm delivery of your verification email. Please try again.
|
||||
{% endtrans %}
|
||||
{% elif state == "confirmation_pending" %}
|
||||
{% trans email=request.user.email %}
|
||||
|
@ -45,12 +35,54 @@
|
|||
{% endtrans %}
|
||||
{% elif state == "confirmation_invalid" %}
|
||||
{% trans email=request.user.email %}
|
||||
The provided code is invalid. Please use the link in the email sent to your email address {{ email }}.
|
||||
- could be unauthorized
|
||||
- could be expired
|
||||
- could be a mistake
|
||||
The provided code is invalid, unauthorized, expired or incomplete. Please use the link in the email sent to your email address {{ email }}. If the code is still not working, please request a new email.
|
||||
|
||||
|
||||
{% endtrans %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if found_emails|length > 0 %}
|
||||
<div class="verify-email-table">
|
||||
<p>
|
||||
{% trans %}
|
||||
We have attempted to send your verification email.
|
||||
Below are the confirmation records we found and their associated delivery statuses.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
<table border=1 frame=void rules=rows>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Date') }}</th>
|
||||
<th>{{ _('From') }}</th>
|
||||
<th>{{ _('To') }}</th>
|
||||
<th>{{ _('Subject') }}</th>
|
||||
<th>{{ _('Status') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for email in found_emails %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ email.statusDate }}
|
||||
</td>
|
||||
<td>
|
||||
{{ email.from }}
|
||||
</td>
|
||||
<td>
|
||||
{{ email.to }}
|
||||
</td>
|
||||
<td>
|
||||
{{ email.subject }}
|
||||
</td>
|
||||
<td>
|
||||
{{ email.status }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if render_button %}
|
||||
{% with submit_text=button_text %}
|
||||
{% include 'devhub/verify_email_form.html' %}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<form action="{{ url('devhub.email_verification') }}" method="post">
|
||||
{% csrf_token %}
|
||||
<button class="Button" type="submit">
|
||||
<button class="Button verify-email-button" type="submit">
|
||||
{{ submit_text | default('Verify email address') }}
|
||||
</button>
|
||||
</form>
|
||||
|
|
|
@ -2255,7 +2255,9 @@ class TestVerifyEmail(TestCase):
|
|||
doc = pq(response.content)
|
||||
assert doc('#suppressed-email').length == 0
|
||||
|
||||
def test_get_confirmation_complete(self):
|
||||
@mock.patch('olympia.devhub.views.check_suppressed_email_confirmation')
|
||||
def test_get_confirmation_complete(self, mock_check_emails):
|
||||
mock_check_emails.return_value = []
|
||||
self.with_email_verification()
|
||||
code = self.email_verification.confirmation_code
|
||||
url = f'{self.url}?code={code}'
|
||||
|
@ -2268,7 +2270,9 @@ class TestVerifyEmail(TestCase):
|
|||
assert 'Your email was successfully verified.' in mail.outbox[0].body
|
||||
self.assert3xx(response, reverse('devhub.email_verification'))
|
||||
|
||||
def test_get_confirmation_complete_with_timeout(self):
|
||||
@mock.patch('olympia.devhub.views.check_suppressed_email_confirmation')
|
||||
def test_get_confirmation_complete_with_timeout(self, mock_check_emails):
|
||||
mock_check_emails.return_value = []
|
||||
self.with_email_verification()
|
||||
code = self.email_verification.confirmation_code
|
||||
url = f'{self.url}?code={code}'
|
||||
|
@ -2296,8 +2300,11 @@ class TestVerifyEmail(TestCase):
|
|||
doc = pq(response.content)
|
||||
|
||||
assert 'Please verify your email' in doc.text()
|
||||
assert 'Verify email' in doc.text()
|
||||
|
||||
def test_get_verification_expired(self):
|
||||
@mock.patch('olympia.devhub.views.check_suppressed_email_confirmation')
|
||||
def test_get_verification_expired(self, mock_check_emails):
|
||||
mock_check_emails.return_value = []
|
||||
self.with_email_verification()
|
||||
|
||||
with freezegun.freeze_time(self.email_verification.created) as frozen_time:
|
||||
|
@ -2307,26 +2314,37 @@ class TestVerifyEmail(TestCase):
|
|||
doc = pq(response.content)
|
||||
|
||||
assert 'Could not verify email address.' in doc.text()
|
||||
assert 'Send another email' in doc.text()
|
||||
|
||||
def test_get_verification_pending(self):
|
||||
@mock.patch('olympia.devhub.views.check_suppressed_email_confirmation')
|
||||
def test_get_verification_pending_without_emails(self, mock_check_emails):
|
||||
mock_check_emails.return_value = []
|
||||
self.with_email_verification()
|
||||
response = self.client.get(self.url)
|
||||
doc = pq(response.content)
|
||||
|
||||
assert 'Working... Please be patient.' in doc.text()
|
||||
assert 'We are sending an email to you' in doc.text()
|
||||
assert 'Send another email' in doc.text()
|
||||
|
||||
def test_get_verification_failed(self):
|
||||
@mock.patch('olympia.devhub.views.check_suppressed_email_confirmation')
|
||||
def test_get_verification_pending_with_emails(self, mock_check_emails):
|
||||
mock_check_emails.return_value = [
|
||||
{'status': 'Delivered', 'subject': 'subject', 'from': 'from', 'to': 'to'}
|
||||
]
|
||||
self.with_email_verification()
|
||||
self.email_verification.status = (
|
||||
SuppressedEmailVerification.STATUS_CHOICES.Failed
|
||||
)
|
||||
self.email_verification.save()
|
||||
response = self.client.get(self.url)
|
||||
doc = pq(response.content)
|
||||
|
||||
assert 'Failed to send confirmation email.' in doc.text()
|
||||
assert 'We have attempted to send your verification' in doc.text()
|
||||
assert 'Delivered' in doc.text()
|
||||
assert 'subject' in doc.text()
|
||||
assert 'from' in doc.text()
|
||||
assert 'to' in doc.text()
|
||||
assert 'Send another email' in doc.text()
|
||||
|
||||
def test_get_verification_timedout(self):
|
||||
@mock.patch('olympia.devhub.views.check_suppressed_email_confirmation')
|
||||
def test_get_verification_timedout(self, mock_check_emails):
|
||||
mock_check_emails.return_value = []
|
||||
self.with_email_verification()
|
||||
|
||||
with freezegun.freeze_time(self.email_verification.created) as frozen_time:
|
||||
|
@ -2337,9 +2355,12 @@ class TestVerifyEmail(TestCase):
|
|||
response = self.client.get(self.url)
|
||||
doc = pq(response.content)
|
||||
|
||||
assert 'This is taking longer than expected.' in doc.text()
|
||||
assert 'It is taking longer than expected' in doc.text()
|
||||
assert 'Send another email' in doc.text()
|
||||
|
||||
def test_get_verification_delivered(self):
|
||||
@mock.patch('olympia.devhub.views.check_suppressed_email_confirmation')
|
||||
def test_get_verification_delivered(self, mock_check_suppressed):
|
||||
mock_check_suppressed.return_value = []
|
||||
self.with_email_verification()
|
||||
self.email_verification.status = (
|
||||
SuppressedEmailVerification.STATUS_CHOICES.Delivered
|
||||
|
@ -2350,11 +2371,17 @@ class TestVerifyEmail(TestCase):
|
|||
|
||||
assert 'An email with a confirmation link has been sent' in doc.text()
|
||||
|
||||
def test_get_confirmation_invalid(self):
|
||||
@mock.patch('olympia.devhub.views.check_suppressed_email_confirmation')
|
||||
def test_get_confirmation_invalid(self, mock_check_emails):
|
||||
mock_check_emails.return_value = []
|
||||
self.with_email_verification()
|
||||
code = 'invalid'
|
||||
url = f'{self.url}?code={code}'
|
||||
response = self.client.get(url)
|
||||
doc = pq(response.content)
|
||||
|
||||
assert 'The provided code is invalid.' in doc.text()
|
||||
assert (
|
||||
'The provided code is invalid, unauthorized, expired or incomplete.'
|
||||
in doc.text()
|
||||
)
|
||||
assert 'Send another email' in doc.text()
|
||||
|
|
|
@ -78,6 +78,7 @@ from olympia.users.models import (
|
|||
from olympia.users.tasks import send_suppressed_email_confirmation
|
||||
from olympia.users.utils import (
|
||||
RestrictionChecker,
|
||||
check_suppressed_email_confirmation,
|
||||
send_addon_author_add_mail,
|
||||
send_addon_author_change_mail,
|
||||
send_addon_author_remove_mail,
|
||||
|
@ -2086,7 +2087,6 @@ VERIFY_EMAIL_STATE = {
|
|||
'email_suppressed': 'email_suppressed',
|
||||
'verification_expired': 'verification_expired',
|
||||
'verification_pending': 'verification_pending',
|
||||
'verification_failed': 'verification_failed',
|
||||
'verification_timedout': 'verification_timedout',
|
||||
'confirmation_pending': 'confirmation_pending',
|
||||
'confirmation_invalid': 'confirmation_invalid',
|
||||
|
@ -2095,7 +2095,7 @@ VERIFY_EMAIL_STATE = {
|
|||
RENDER_BUTTON_STATES = [
|
||||
VERIFY_EMAIL_STATE['email_suppressed'],
|
||||
VERIFY_EMAIL_STATE['verification_expired'],
|
||||
VERIFY_EMAIL_STATE['verification_failed'],
|
||||
VERIFY_EMAIL_STATE['verification_pending'],
|
||||
VERIFY_EMAIL_STATE['verification_timedout'],
|
||||
VERIFY_EMAIL_STATE['confirmation_invalid'],
|
||||
]
|
||||
|
@ -2105,7 +2105,7 @@ def get_button_text(state):
|
|||
if state == VERIFY_EMAIL_STATE['email_suppressed']:
|
||||
return gettext('Verify email')
|
||||
|
||||
return gettext('Try again')
|
||||
return gettext('Send another email')
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -2130,6 +2130,7 @@ def email_verification(request):
|
|||
return redirect('devhub.email_verification')
|
||||
|
||||
if email_verification:
|
||||
data['found_emails'] = check_suppressed_email_confirmation(email_verification)
|
||||
if email_verification.is_expired:
|
||||
data['state'] = VERIFY_EMAIL_STATE['verification_expired']
|
||||
elif code := request.GET.get('code'):
|
||||
|
@ -2144,24 +2145,16 @@ def email_verification(request):
|
|||
return redirect('devhub.email_verification')
|
||||
else:
|
||||
data['state'] = VERIFY_EMAIL_STATE['confirmation_invalid']
|
||||
elif (
|
||||
email_verification.status
|
||||
== SuppressedEmailVerification.STATUS_CHOICES.Pending
|
||||
):
|
||||
if email_verification.is_timedout:
|
||||
data['state'] = VERIFY_EMAIL_STATE['verification_timedout']
|
||||
elif email_verification.is_timedout:
|
||||
data['state'] = VERIFY_EMAIL_STATE['verification_timedout']
|
||||
else:
|
||||
if (
|
||||
email_verification.reload().status
|
||||
== SuppressedEmailVerification.STATUS_CHOICES.Delivered
|
||||
):
|
||||
data['state'] = VERIFY_EMAIL_STATE['confirmation_pending']
|
||||
else:
|
||||
data['state'] = VERIFY_EMAIL_STATE['verification_pending']
|
||||
elif (
|
||||
email_verification.status
|
||||
== SuppressedEmailVerification.STATUS_CHOICES.Failed
|
||||
):
|
||||
data['state'] = VERIFY_EMAIL_STATE['verification_failed']
|
||||
elif (
|
||||
email_verification.status
|
||||
== SuppressedEmailVerification.STATUS_CHOICES.Delivered
|
||||
):
|
||||
data['state'] = VERIFY_EMAIL_STATE['confirmation_pending']
|
||||
|
||||
elif suppressed_email:
|
||||
data['state'] = VERIFY_EMAIL_STATE['email_suppressed']
|
||||
|
|
|
@ -924,7 +924,6 @@ CELERY_TASK_ROUTES = {
|
|||
'olympia.reviewers.tasks.recalculate_post_review_weight': {'queue': 'cron'},
|
||||
'olympia.users.tasks.sync_suppressed_emails_task': {'queue': 'cron'},
|
||||
'olympia.users.tasks.send_suppressed_email_confirmation': {'queue': 'devhub'},
|
||||
'olympia.users.tasks.check_suppressed_email_confirmation': {'queue': 'devhub'},
|
||||
# Reviewers.
|
||||
'olympia.lib.crypto.tasks.sign_addons': {'queue': 'reviewers'},
|
||||
# Admin.
|
||||
|
|
|
@ -1386,3 +1386,7 @@ class SuppressedEmailVerification(ModelBase):
|
|||
@property
|
||||
def is_timedout(self):
|
||||
return self.created + timedelta(seconds=30) < datetime.now()
|
||||
|
||||
def mark_as_delivered(self):
|
||||
self.update(status=SuppressedEmailVerification.STATUS_CHOICES.Delivered)
|
||||
self.save()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import csv
|
||||
import datetime
|
||||
import itertools
|
||||
import tempfile
|
||||
import urllib.parse
|
||||
|
@ -9,7 +8,6 @@ from django.urls import reverse
|
|||
from django.utils.translation import gettext
|
||||
|
||||
import requests
|
||||
from celery.exceptions import Retry
|
||||
from requests.exceptions import HTTPError, Timeout
|
||||
|
||||
import olympia.core.logger
|
||||
|
@ -30,6 +28,7 @@ from .models import (
|
|||
SuppressedEmailVerification,
|
||||
UserProfile,
|
||||
)
|
||||
from .utils import assert_socket_labs_settings_defined
|
||||
|
||||
|
||||
task_log = olympia.core.logger.getLogger('z.task')
|
||||
|
@ -91,17 +90,6 @@ def update_user_ratings_task(data, **kw):
|
|||
BATCH_SIZE = 100
|
||||
|
||||
|
||||
def assert_socket_labs_settings_defined():
|
||||
if not settings.SOCKET_LABS_TOKEN:
|
||||
raise Exception('SOCKET_LABS_TOKEN is not defined')
|
||||
|
||||
if not settings.SOCKET_LABS_HOST:
|
||||
raise Exception('SOCKET_LABS_HOST is not defined')
|
||||
|
||||
if not settings.SOCKET_LABS_SERVER_ID:
|
||||
raise Exception('SOCKET_LABS_SERVER_ID is not defined')
|
||||
|
||||
|
||||
@task(autoretry_for=(HTTPError, Timeout), max_retries=5, retry_backoff=True)
|
||||
def sync_suppressed_emails_task(batch_size=BATCH_SIZE, **kw):
|
||||
assert_socket_labs_settings_defined()
|
||||
|
@ -186,6 +174,8 @@ def send_suppressed_email_confirmation(suppressed_email_verification_id):
|
|||
else:
|
||||
response.raise_for_status()
|
||||
|
||||
task_log.info('email removed from suppression')
|
||||
|
||||
code_snippet = str(verification.confirmation_code)[-5:]
|
||||
|
||||
verification.status = SuppressedEmailVerification.STATUS_CHOICES.Pending
|
||||
|
@ -206,113 +196,3 @@ def send_suppressed_email_confirmation(suppressed_email_verification_id):
|
|||
)
|
||||
|
||||
verification.save()
|
||||
check_suppressed_email_confirmation.delay(verification.id)
|
||||
|
||||
|
||||
@task(
|
||||
autoretry_for=(
|
||||
HTTPError,
|
||||
Timeout,
|
||||
),
|
||||
max_retries=5,
|
||||
retry_backoff=True,
|
||||
)
|
||||
def check_suppressed_email_confirmation(suppressed_email_verification_id, page_size=5):
|
||||
assert_socket_labs_settings_defined()
|
||||
|
||||
verification = SuppressedEmailVerification.objects.filter(
|
||||
id=suppressed_email_verification_id
|
||||
).first()
|
||||
|
||||
if not verification:
|
||||
raise Exception(f'invalid id: {suppressed_email_verification_id}')
|
||||
|
||||
email = verification.suppressed_email.email
|
||||
|
||||
current_count = 0
|
||||
total = 0
|
||||
|
||||
code_snippet = str(verification.confirmation_code)[-5:]
|
||||
path = f'servers/{settings.SOCKET_LABS_SERVER_ID}/reports/recipient-search/'
|
||||
|
||||
# socketlabs might set the queued time any time of day
|
||||
# so we need to check to midnight, one day before the verification was created
|
||||
# and to midnight of tomorrow
|
||||
before = verification.created - datetime.timedelta(days=1)
|
||||
start_date = datetime.datetime(
|
||||
year=before.year,
|
||||
month=before.month,
|
||||
day=before.day,
|
||||
)
|
||||
end_date = datetime.datetime.now() + datetime.timedelta(days=1)
|
||||
date_format = '%Y-%m-%d'
|
||||
|
||||
params = {
|
||||
'toEmailAddress': email,
|
||||
'startDate': start_date.strftime(date_format),
|
||||
'endDate': end_date.strftime(date_format),
|
||||
'pageNumber': 0,
|
||||
'pageSize': page_size,
|
||||
'sortField': 'queuedTime',
|
||||
'sortDirection': 'dsc',
|
||||
}
|
||||
|
||||
is_first_page = True
|
||||
|
||||
while current_count < total or is_first_page:
|
||||
if not is_first_page:
|
||||
params['pageNumber'] = params['pageNumber'] + 1
|
||||
|
||||
url = (
|
||||
urllib.parse.urljoin(settings.SOCKET_LABS_HOST, path)
|
||||
+ '?'
|
||||
+ urllib.parse.urlencode(params)
|
||||
)
|
||||
|
||||
headers = {
|
||||
'authorization': f'Bearer {settings.SOCKET_LABS_TOKEN}',
|
||||
}
|
||||
|
||||
task_log.info(f'checking for {code_snippet} with params {params}')
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
json = response.json()
|
||||
|
||||
if is_first_page:
|
||||
total = json['total']
|
||||
|
||||
if total == 0:
|
||||
raise Retry(
|
||||
f'No emails found for email {email}.'
|
||||
'retrying as email could not be queued yet'
|
||||
)
|
||||
|
||||
is_first_page = False
|
||||
|
||||
data = json['data']
|
||||
current_count += len(data)
|
||||
|
||||
## TODO: check if we can set `customMessageId` to replace code snippet
|
||||
for item in data:
|
||||
if code_snippet in item['subject']:
|
||||
options = dict(SuppressedEmailVerification.STATUS_CHOICES).values()
|
||||
new_status = item['status']
|
||||
if new_status not in options:
|
||||
raise Exception(
|
||||
f'invalid status: {new_status} '
|
||||
f'for {suppressed_email_verification_id}. '
|
||||
f'expected {", ".join(options)}'
|
||||
)
|
||||
|
||||
task_log.info(f'Found matching email {item}')
|
||||
|
||||
verification.update(
|
||||
status=SuppressedEmailVerification.STATUS_CHOICES[item['status']]
|
||||
)
|
||||
return
|
||||
|
||||
raise Retry(
|
||||
f'failed to find email for code: {code_snippet} in {total} emails.'
|
||||
'retrying as email could not be queued yet'
|
||||
)
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import csv
|
||||
import io
|
||||
import json
|
||||
import shutil
|
||||
import tempfile
|
||||
import uuid
|
||||
from unittest import mock
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
from django.conf import settings
|
||||
from django.core import mail
|
||||
|
@ -14,7 +12,6 @@ from django.urls import reverse
|
|||
import pytest
|
||||
import responses
|
||||
from celery.exceptions import Retry
|
||||
from freezegun import freeze_time
|
||||
from PIL import Image
|
||||
from requests.exceptions import Timeout
|
||||
|
||||
|
@ -28,7 +25,6 @@ from olympia.users.models import (
|
|||
SuppressedEmailVerification,
|
||||
)
|
||||
from olympia.users.tasks import (
|
||||
check_suppressed_email_confirmation,
|
||||
delete_photo,
|
||||
resize_photo,
|
||||
send_suppressed_email_confirmation,
|
||||
|
@ -326,8 +322,7 @@ class TestSendSuppressedEmailConfirmation(TestCase):
|
|||
with pytest.raises(Exception, match=f'invalid id: {invalid_id}'):
|
||||
send_suppressed_email_confirmation.apply([invalid_id])
|
||||
|
||||
@mock.patch('olympia.users.tasks.check_suppressed_email_confirmation')
|
||||
def test_socket_labs_returns_404(self, mock_check_suppressed_email_confirmation):
|
||||
def test_socket_labs_returns_404(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
|
@ -343,15 +338,11 @@ class TestSendSuppressedEmailConfirmation(TestCase):
|
|||
status=404,
|
||||
)
|
||||
|
||||
mock_check_suppressed_email_confirmation.delay.return_value = None
|
||||
|
||||
try:
|
||||
send_suppressed_email_confirmation.apply([verification.id])
|
||||
except Exception as err:
|
||||
pytest.fail('Unexpected exception: {0}'.format(err))
|
||||
|
||||
assert mock_check_suppressed_email_confirmation.delay.call_count == 1
|
||||
|
||||
def test_socket_labs_returns_5xx(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
|
@ -371,8 +362,7 @@ class TestSendSuppressedEmailConfirmation(TestCase):
|
|||
with pytest.raises(Retry):
|
||||
send_suppressed_email_confirmation.apply([verification.id])
|
||||
|
||||
@mock.patch('olympia.users.tasks.check_suppressed_email_confirmation')
|
||||
def test_email_sent(self, mock_check_suppressed_email_confirmation):
|
||||
def test_email_sent(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
|
@ -387,8 +377,6 @@ class TestSendSuppressedEmailConfirmation(TestCase):
|
|||
status=201,
|
||||
)
|
||||
|
||||
mock_check_suppressed_email_confirmation.delay.return_value = None
|
||||
|
||||
send_suppressed_email_confirmation.apply([verification.id])
|
||||
|
||||
assert len(mail.outbox) == 1
|
||||
|
@ -400,12 +388,9 @@ class TestSendSuppressedEmailConfirmation(TestCase):
|
|||
)
|
||||
assert expected_confirmation_link in mail.outbox[0].body
|
||||
assert str(verification.confirmation_code)[-5:] in mail.outbox[0].subject
|
||||
assert mock_check_suppressed_email_confirmation.delay.call_count == 1
|
||||
|
||||
@mock.patch('olympia.users.tasks.check_suppressed_email_confirmation')
|
||||
def test_retry_existing_verification(
|
||||
self,
|
||||
mock_check_suppressed_email_confirmation,
|
||||
):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
|
@ -423,345 +408,9 @@ class TestSendSuppressedEmailConfirmation(TestCase):
|
|||
status=201,
|
||||
)
|
||||
|
||||
mock_check_suppressed_email_confirmation.delay.return_value = None
|
||||
|
||||
assert verification.status == SuppressedEmailVerification.STATUS_CHOICES.Failed
|
||||
send_suppressed_email_confirmation.apply([verification.id])
|
||||
assert (
|
||||
verification.reload().status
|
||||
== SuppressedEmailVerification.STATUS_CHOICES.Pending
|
||||
)
|
||||
|
||||
|
||||
class TestCheckSuppressedEmailConfirmation(TestCase):
|
||||
def setUp(self):
|
||||
self.user_profile = user_factory()
|
||||
|
||||
def test_fails_missing_settings(self):
|
||||
for setting in (
|
||||
'SOCKET_LABS_TOKEN',
|
||||
'SOCKET_LABS_HOST',
|
||||
'SOCKET_LABS_SERVER_ID',
|
||||
):
|
||||
with pytest.raises(Exception) as exc:
|
||||
setattr(settings, setting, None)
|
||||
check_suppressed_email_confirmation.apply(1)
|
||||
assert exc.match('SOCKET_LABS_TOKEN is not defined')
|
||||
|
||||
def test_no_verification_for_id(self):
|
||||
invalid_id = 1
|
||||
|
||||
assert SuppressedEmailVerification.objects.all().count() == 0
|
||||
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match=f'invalid id: {invalid_id}',
|
||||
):
|
||||
check_suppressed_email_confirmation.apply([invalid_id])
|
||||
|
||||
def test_socket_labs_returns_5xx(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
),
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
(
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
),
|
||||
status=500,
|
||||
)
|
||||
|
||||
with pytest.raises(Retry):
|
||||
check_suppressed_email_confirmation.apply([verification.id])
|
||||
|
||||
def test_socket_labs_returns_empty(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
),
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
(
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
),
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [],
|
||||
'total': 0,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
with pytest.raises(Retry) as error_info:
|
||||
check_suppressed_email_confirmation.apply([verification.id])
|
||||
|
||||
assert f'No emails found for email {self.user_profile.email}' in str(
|
||||
error_info.value
|
||||
)
|
||||
|
||||
def test_auth_header_present(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
),
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
(
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
),
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [],
|
||||
'total': 0,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
with pytest.raises(Retry):
|
||||
check_suppressed_email_confirmation.apply([verification.id])
|
||||
|
||||
assert (
|
||||
settings.SOCKET_LABS_TOKEN
|
||||
in responses.calls[0].request.headers['authorization']
|
||||
)
|
||||
|
||||
@freeze_time('2023-06-26 11:00')
|
||||
def test_format_date_params(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
),
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
(
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
),
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [],
|
||||
'total': 0,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
with pytest.raises(Retry):
|
||||
check_suppressed_email_confirmation.apply([verification.id])
|
||||
|
||||
parsed_url = urlparse(responses.calls[0].request.url)
|
||||
search_params = parse_qs(parsed_url.query)
|
||||
|
||||
assert search_params['startDate'][0] == '2023-06-25'
|
||||
assert search_params['endDate'][0] == '2023-06-27'
|
||||
|
||||
def test_pagination(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
),
|
||||
)
|
||||
|
||||
response_size = 5
|
||||
|
||||
body = [{'subject': 'test'} for _ in range(response_size)]
|
||||
|
||||
url = (
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': body,
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
code_snippet = str(verification.confirmation_code)[-5:]
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [
|
||||
{
|
||||
'subject': f'test {code_snippet}',
|
||||
'status': 'Delivered',
|
||||
}
|
||||
],
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
check_suppressed_email_confirmation.apply([verification.id, response_size])
|
||||
|
||||
assert len(responses.calls) == 2
|
||||
|
||||
def test_found_email(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
),
|
||||
)
|
||||
|
||||
response_size = 5
|
||||
|
||||
body = [{'subject': 'test'} for _ in range(response_size)]
|
||||
|
||||
url = (
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': body,
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
code_snippet = str(verification.confirmation_code)[-5:]
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [
|
||||
{
|
||||
'subject': f'test {code_snippet}',
|
||||
'status': 'Delivered',
|
||||
}
|
||||
],
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
check_suppressed_email_confirmation.apply([verification.id, response_size])
|
||||
|
||||
assert (
|
||||
verification.reload().status
|
||||
== SuppressedEmailVerification.STATUS_CHOICES.Delivered
|
||||
)
|
||||
|
||||
def test_invalid_status(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
),
|
||||
)
|
||||
|
||||
response_size = 5
|
||||
|
||||
body = [{'subject': 'test'} for _ in range(response_size)]
|
||||
|
||||
url = (
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': body,
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
code_snippet = str(verification.confirmation_code)[-5:]
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [
|
||||
{
|
||||
'subject': f'test {code_snippet}',
|
||||
'status': 'InvalidStsatus',
|
||||
}
|
||||
],
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
with pytest.raises(Exception) as exc:
|
||||
check_suppressed_email_confirmation.apply([verification.id, response_size])
|
||||
|
||||
exc_msg = str(exc.value)
|
||||
assert f'invalid status: InvalidStsatus for {verification.id}' in exc_msg
|
||||
for status in dict(SuppressedEmailVerification.STATUS_CHOICES).values():
|
||||
assert status in exc_msg
|
||||
|
||||
def test_rsponse_does_not_contain_suppressed_email(self):
|
||||
verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
),
|
||||
)
|
||||
|
||||
response_size = 5
|
||||
|
||||
body = [{'subject': 'test'} for _ in range(response_size)]
|
||||
|
||||
url = (
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': body,
|
||||
'total': response_size,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
with pytest.raises(Retry) as error_info:
|
||||
check_suppressed_email_confirmation.apply([verification.id, response_size])
|
||||
|
||||
assert 'failed to find email for code: ' in str(error_info.value)
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import json
|
||||
from contextlib import ExitStack
|
||||
from ipaddress import IPv4Address
|
||||
from unittest import mock
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
import pytest
|
||||
import responses
|
||||
from freezegun import freeze_time
|
||||
|
||||
from olympia import amo, core
|
||||
from olympia.activity.models import ActivityLog
|
||||
|
@ -16,9 +21,15 @@ from ..models import (
|
|||
RESTRICTION_TYPES,
|
||||
EmailUserRestriction,
|
||||
IPNetworkUserRestriction,
|
||||
SuppressedEmail,
|
||||
SuppressedEmailVerification,
|
||||
UserRestrictionHistory,
|
||||
)
|
||||
from ..utils import RestrictionChecker, UnsubscribeCode
|
||||
from ..utils import (
|
||||
RestrictionChecker,
|
||||
UnsubscribeCode,
|
||||
check_suppressed_email_confirmation,
|
||||
)
|
||||
|
||||
|
||||
def test_email_unsubscribe_code_parse():
|
||||
|
@ -298,3 +309,326 @@ class TestRestrictionChecker(TestCase):
|
|||
assert restriction_mock.call_count == 0
|
||||
for restriction_mock in allow_auto_approval_mocks:
|
||||
assert restriction_mock.call_count == 1
|
||||
|
||||
|
||||
class TestCheckSuppressedEmailConfirmation(TestCase):
|
||||
def setUp(self):
|
||||
self.verification = None
|
||||
self.user_profile = user_factory()
|
||||
|
||||
def with_verification(self):
|
||||
self.verification = SuppressedEmailVerification.objects.create(
|
||||
suppressed_email=SuppressedEmail.objects.create(
|
||||
email=self.user_profile.email
|
||||
)
|
||||
)
|
||||
|
||||
def fake_email_response(self, code='', status='Suppressed'):
|
||||
return {
|
||||
'subject': f'test {code}',
|
||||
'status': status,
|
||||
'from': 'from',
|
||||
'to': 'to',
|
||||
'statusDate': '2023-06-26T11:00:00Z',
|
||||
}
|
||||
|
||||
def test_fails_missing_settings(self):
|
||||
self.with_verification()
|
||||
for setting in (
|
||||
'SOCKET_LABS_TOKEN',
|
||||
'SOCKET_LABS_HOST',
|
||||
'SOCKET_LABS_SERVER_ID',
|
||||
):
|
||||
with pytest.raises(Exception) as exc:
|
||||
setattr(settings, setting, None)
|
||||
check_suppressed_email_confirmation(self.verification)
|
||||
assert exc.match(f'{setting} is not defined')
|
||||
|
||||
def test_no_verification(self):
|
||||
assert not self.user_profile.suppressed_email
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
check_suppressed_email_confirmation(self.verification)
|
||||
|
||||
def test_socket_labs_returns_5xx(self):
|
||||
self.with_verification()
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
(
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
),
|
||||
status=500,
|
||||
)
|
||||
|
||||
with pytest.raises(Exception): # noqa: B017
|
||||
check_suppressed_email_confirmation(self.verification)
|
||||
|
||||
def test_socket_labs_returns_empty(self):
|
||||
self.with_verification()
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
(
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
),
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [],
|
||||
'total': 0,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
assert len(check_suppressed_email_confirmation(self.verification)) == 0
|
||||
|
||||
def test_auth_header_present(self):
|
||||
self.with_verification()
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
(
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
),
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [],
|
||||
'total': 0,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
check_suppressed_email_confirmation(self.verification)
|
||||
|
||||
assert (
|
||||
settings.SOCKET_LABS_TOKEN
|
||||
in responses.calls[0].request.headers['authorization']
|
||||
)
|
||||
|
||||
@freeze_time('2023-06-26 11:00')
|
||||
def test_format_date_params(self):
|
||||
self.with_verification()
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
(
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
),
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [],
|
||||
'total': 0,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
check_suppressed_email_confirmation(self.verification)
|
||||
|
||||
parsed_url = urlparse(responses.calls[0].request.url)
|
||||
search_params = parse_qs(parsed_url.query)
|
||||
|
||||
assert search_params['startDate'][0] == '2023-06-25'
|
||||
assert search_params['endDate'][0] == '2023-06-27'
|
||||
|
||||
def test_pagination(self):
|
||||
self.with_verification()
|
||||
|
||||
response_size = 5
|
||||
|
||||
body = [self.fake_email_response() for _ in range(response_size)]
|
||||
|
||||
url = (
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': body,
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
code_snippet = str(self.verification.confirmation_code)[-5:]
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [self.fake_email_response(code_snippet)],
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
check_suppressed_email_confirmation(self.verification, response_size)
|
||||
|
||||
assert len(responses.calls) == 2
|
||||
|
||||
def test_found_email(self):
|
||||
self.with_verification()
|
||||
|
||||
response_size = 5
|
||||
|
||||
body = [self.fake_email_response() for _ in range(response_size)]
|
||||
|
||||
url = (
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': body,
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
code_snippet = str(self.verification.confirmation_code)[-5:]
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [self.fake_email_response(code_snippet, 'Delivered')],
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
check_suppressed_email_confirmation(self.verification, response_size)
|
||||
|
||||
assert (
|
||||
self.verification.reload().status
|
||||
== SuppressedEmailVerification.STATUS_CHOICES.Delivered
|
||||
)
|
||||
|
||||
def test_verify_response_data(self):
|
||||
self.with_verification()
|
||||
|
||||
response_size = 1
|
||||
|
||||
url = (
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
)
|
||||
|
||||
code_snippet = str(self.verification.confirmation_code)[-5:]
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [self.fake_email_response(code_snippet, 'Delivered')],
|
||||
'total': response_size,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
result = check_suppressed_email_confirmation(self.verification, response_size)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0]['subject'] == f'test {code_snippet}'
|
||||
assert result[0]['status'] == 'Delivered'
|
||||
assert result[0]['from'] == 'from'
|
||||
assert result[0]['to'] == 'to'
|
||||
assert result[0]['statusDate'] == '2023-06-26T11:00:00Z'
|
||||
|
||||
def test_not_delivered_status(self):
|
||||
self.with_verification()
|
||||
|
||||
response_size = 5
|
||||
|
||||
body = [self.fake_email_response() for _ in range(response_size)]
|
||||
|
||||
url = (
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': body,
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
code_snippet = str(self.verification.confirmation_code)[-5:]
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': [self.fake_email_response(code_snippet, 'InvalidStatus')],
|
||||
'total': response_size + 1,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
result = check_suppressed_email_confirmation(self.verification, response_size)
|
||||
|
||||
assert len(result) == 1
|
||||
|
||||
assert result[0]['status'] == 'InvalidStatus'
|
||||
|
||||
def test_rsponse_does_not_contain_suppressed_email(self):
|
||||
self.with_verification()
|
||||
|
||||
response_size = 5
|
||||
|
||||
body = [self.fake_email_response() for _ in range(response_size)]
|
||||
|
||||
url = (
|
||||
f'{settings.SOCKET_LABS_HOST}servers/{settings.SOCKET_LABS_SERVER_ID}/'
|
||||
f'reports/recipient-search/'
|
||||
)
|
||||
|
||||
responses.add(
|
||||
responses.GET,
|
||||
url,
|
||||
status=200,
|
||||
body=json.dumps(
|
||||
{
|
||||
'data': body,
|
||||
'total': response_size,
|
||||
}
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
result = check_suppressed_email_confirmation(self.verification, response_size)
|
||||
|
||||
assert len(result) == 0
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import base64
|
||||
import datetime
|
||||
import hashlib
|
||||
import hmac
|
||||
import urllib
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
@ -9,6 +11,7 @@ from django.urls import reverse
|
|||
from django.utils.encoding import force_bytes, force_str
|
||||
from django.utils.translation import gettext
|
||||
|
||||
import requests
|
||||
from django_statsd.clients import statsd
|
||||
|
||||
import olympia.core.logger
|
||||
|
@ -285,3 +288,112 @@ def upload_picture(user, picture):
|
|||
user.picture_path,
|
||||
set_modified_on=user.serializable_reference(),
|
||||
)
|
||||
|
||||
|
||||
def assert_socket_labs_settings_defined():
|
||||
if not settings.SOCKET_LABS_TOKEN:
|
||||
raise Exception('SOCKET_LABS_TOKEN is not defined')
|
||||
|
||||
if not settings.SOCKET_LABS_HOST:
|
||||
raise Exception('SOCKET_LABS_HOST is not defined')
|
||||
|
||||
if not settings.SOCKET_LABS_SERVER_ID:
|
||||
raise Exception('SOCKET_LABS_SERVER_ID is not defined')
|
||||
|
||||
|
||||
utils_log = olympia.core.logger.getLogger('z.users')
|
||||
|
||||
|
||||
def check_suppressed_email_confirmation(verification, page_size=5):
|
||||
from olympia.users.models import SuppressedEmailVerification
|
||||
|
||||
assert_socket_labs_settings_defined()
|
||||
|
||||
assert isinstance(verification, SuppressedEmailVerification)
|
||||
|
||||
email = verification.suppressed_email.email
|
||||
|
||||
current_count = 0
|
||||
total = 0
|
||||
|
||||
code_snippet = str(verification.confirmation_code)[-5:]
|
||||
path = f'servers/{settings.SOCKET_LABS_SERVER_ID}/reports/recipient-search/'
|
||||
|
||||
# socketlabs might set the queued time any time of day
|
||||
# so we need to check to midnight, one day before the verification was created
|
||||
# and to midnight of tomorrow
|
||||
before = verification.created - datetime.timedelta(days=1)
|
||||
start_date = datetime.datetime(
|
||||
year=before.year,
|
||||
month=before.month,
|
||||
day=before.day,
|
||||
)
|
||||
end_date = datetime.datetime.now() + datetime.timedelta(days=1)
|
||||
date_format = '%Y-%m-%d'
|
||||
|
||||
params = {
|
||||
'toEmailAddress': email,
|
||||
'startDate': start_date.strftime(date_format),
|
||||
'endDate': end_date.strftime(date_format),
|
||||
'pageNumber': 0,
|
||||
'pageSize': page_size,
|
||||
'sortField': 'queuedTime',
|
||||
'sortDirection': 'dsc',
|
||||
}
|
||||
|
||||
is_first_page = True
|
||||
|
||||
found_emails = []
|
||||
|
||||
while current_count < total or is_first_page:
|
||||
if not is_first_page:
|
||||
params['pageNumber'] = params['pageNumber'] + 1
|
||||
|
||||
url = (
|
||||
urllib.parse.urljoin(settings.SOCKET_LABS_HOST, path)
|
||||
+ '?'
|
||||
+ urllib.parse.urlencode(params)
|
||||
)
|
||||
|
||||
headers = {
|
||||
'authorization': f'Bearer {settings.SOCKET_LABS_TOKEN}',
|
||||
}
|
||||
|
||||
utils_log.info(f'checking for {code_snippet} with params {params}')
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
json_data = response.json()
|
||||
|
||||
utils_log.info(f'recieved data {json_data} for {code_snippet}')
|
||||
|
||||
if is_first_page:
|
||||
total = json_data['total']
|
||||
|
||||
if total == 0:
|
||||
return found_emails
|
||||
|
||||
is_first_page = False
|
||||
|
||||
data = json_data['data']
|
||||
current_count += len(data)
|
||||
|
||||
utils_log.info(f'found emails {data} for {code_snippet}')
|
||||
|
||||
## TODO: check if we can set `customMessageId` to replace code snippet
|
||||
for item in data:
|
||||
if code_snippet in item['subject']:
|
||||
found_emails.append(
|
||||
{
|
||||
'from': item['from'],
|
||||
'to': item['to'],
|
||||
'subject': item['subject'],
|
||||
'status': item['status'],
|
||||
'statusDate': item['statusDate'],
|
||||
}
|
||||
)
|
||||
|
||||
if item['status'] == 'Delivered':
|
||||
verification.mark_as_delivered()
|
||||
|
||||
return found_emails
|
||||
|
|
|
@ -1507,6 +1507,29 @@ form.select-review .errorlist {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.verify-email-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.verify-email-table {
|
||||
margin: 20px 0;
|
||||
width: 100%;
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid;
|
||||
}
|
||||
tr {
|
||||
border: solid;
|
||||
border-width: 1px 0;
|
||||
}
|
||||
td {
|
||||
padding: 5px;
|
||||
border: solid;
|
||||
border-width: 0 1px;
|
||||
}
|
||||
}
|
||||
|
||||
button.search-button {
|
||||
background: #84c63c url("../../img/icons/go-arrow.png") center no-repeat;
|
||||
background-image: url("../../img/icons/go-arrow.png"), linear-gradient(#84c63c, #489615);
|
||||
|
|
Загрузка…
Ссылка в новой задаче