Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions ietf/api/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,39 @@ def test_active_email_list(self):
self.assertCountEqual(result.keys(), ["addresses"])
self.assertCountEqual(result["addresses"], Email.objects.filter(active=True).values_list("address", flat=True))

@override_settings(APP_API_TOKENS={"ietf.api.views.related_email_list": ["valid-token"]})
def test_related_email_list(self):
joe = EmailFactory(address='joe@work.com')
EmailFactory(address='joe@home.com', person=joe.person)
EmailFactory(address='jòe@spain.com', person=joe.person)
url = urlreverse("ietf.api.views.related_email_list", kwargs={'email': 'joe@home.com'})
# no api key
r = self.client.get(url, headers={})
self.assertEqual(r.status_code, 403)
# invalid api key
r = self.client.get(url, headers={"X-Api-Key": "not-the-valid-token"})
self.assertEqual(r.status_code, 403)
# wrong method
r = self.client.post(url, headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 405)
# valid
r = self.client.get(url, headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.headers["Content-Type"], "application/json")
result = json.loads(r.content)
self.assertCountEqual(result.keys(), ["addresses"])
self.assertCountEqual(result["addresses"], joe.person.email_set.exclude(address='joe@home.com').values_list("address", flat=True))
# non-ascii
non_ascii_url = urlreverse("ietf.api.views.related_email_list", kwargs={'email': 'jòe@spain.com'})
r = self.client.get(non_ascii_url, headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 200)
result = json.loads(r.content)
self.assertTrue('joe@home.com' in result["addresses"])
# email not found
not_found_url = urlreverse("ietf.api.views.related_email_list", kwargs={'email': 'nobody@nowhere.com'})
r = self.client.get(not_found_url, headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 404)

@override_settings(APP_API_TOKENS={"ietf.api.views.role_holder_addresses": ["valid-token"]})
def test_role_holder_addresses(self):
url = urlreverse("ietf.api.views.role_holder_addresses")
Expand Down
2 changes: 2 additions & 0 deletions ietf/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
url(r'^openid/', include('oidc_provider.urls', namespace='oidc_provider')),
# Email alias listing
url(r'^person/email/$', api_views.active_email_list),
# Related Email listing
url(r'^person/email/(?P<email>[^/\x00]+)/related/$', api_views.related_email_list),
# Draft submission API
url(r'^submit/?$', submit_views.api_submit_tombstone),
# Draft upload API
Expand Down
25 changes: 25 additions & 0 deletions ietf/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,31 @@ def active_email_list(request):
return HttpResponse(status=405)


@requires_api_token
@csrf_exempt
def related_email_list(request, email):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a guard against null characters in the email, along the lines of

    if "\x00" in emai:
        return HttpResponseBadRequest()

(or perhaps equivalent using _http_err()). We also need this in some other places in the existing code.

To see the issue, request something with a %00 in the parameter. Note that going to http://localhost:8000/ as we do for normal testing can hide the problem because the nginx proxy in front of datatracker generates its own 400. To get around that, open a shell in the app container and use something like curl 'http://localhost:8001/person/email/bad%00address/related/' -v -o /dev/null. It should return a 400, not a 500 error.

"""Given an email address, returns all other email addresses known
to Datatracker, via Person object
"""
def _http_err(code, text):
return HttpResponse(text, status=code, content_type="text/plain")

if request.method == "GET":
try:
email_obj = Email.objects.get(address=email)
except Email.DoesNotExist:
return _http_err(404, "Email not found")
person = email_obj.person
if not person:
return JsonResponse({"addresses": []})
return JsonResponse(
{
"addresses": list(person.email_set.exclude(address=email).values_list("address", flat=True)),
}
)
return HttpResponse(status=405)


@requires_api_token
def role_holder_addresses(request):
if request.method == "GET":
Expand Down