Skip to content
This repository was archived by the owner on Apr 4, 2023. It is now read-only.

Web API: file upload & proposal_pipeline support, tests, and some cleanup #301

Merged
merged 17 commits into from
Sep 8, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
33dac42
Add support for proposal_pipeline, file uploads. Also add some tests.
tadhg-ohiggins Aug 31, 2016
a3d8937
Improve upload file tests, add tests for proposal_pipeline endpoints.
tadhg-ohiggins Aug 31, 2016
8950026
Clean up web API serializer code.
tadhg-ohiggins Sep 2, 2016
6e980be
Add docstrings to more methods of web API views.
tadhg-ohiggins Sep 2, 2016
d898673
Web API tests: add fake calls so that tests (which aren't testing act…
tadhg-ohiggins Sep 2, 2016
cd39a80
Web API tests: fix Python 2/3 encoding mismatches; stop overriding __…
tadhg-ohiggins Sep 2, 2016
6636491
Web API: fix some minor issues pointed out by CM.
tadhg-ohiggins Sep 2, 2016
4952595
Web API: use email address variable and not my own email address…
tadhg-ohiggins Sep 2, 2016
944df41
Web API: use instead of checking count with filtered .
tadhg-ohiggins Sep 2, 2016
9c71e1e
Web API: add tests for some utility functions, refactor those functions.
tadhg-ohiggins Sep 7, 2016
20d5f97
Web API: per CM's suggestion, move some class properties to instance …
tadhg-ohiggins Sep 7, 2016
a2593a4
Web API: per CM's suggestion, make the filesize limit test deal in mu…
tadhg-ohiggins Sep 7, 2016
8d1d836
Web API: per CM's suggestion, move some class properties to instance …
tadhg-ohiggins Sep 7, 2016
9d4ddf4
Web API: per CM's suggestion, use uuid4() instead of fixed values for…
tadhg-ohiggins Sep 7, 2016
03836ec
Web API: per CM's suggestion, patch at a lower level to minimize requ…
tadhg-ohiggins Sep 7, 2016
10c2fbc
Web API: per CM's suggestion, populate expected results dict with def…
tadhg-ohiggins Sep 7, 2016
58f8246
Web API: per CM's suggestion, split test_create_ints() into helper me…
tadhg-ohiggins Sep 8, 2016
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
58 changes: 38 additions & 20 deletions regparser/web/jobs/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ def add_redis_data_to_job_data(job_data):
return job_data


def status_url(job_id, sub_path=""):
def get_host():
"""
We want to give users a URL for checking the status of a job.
We want to provide users with status URLs, and so need host:port.
While I can't think of an exploit resulting from relying on the host data
from the request if the request were spoofed, we'll be cautious and define
the canonical host data ourselves.
Expand All @@ -95,38 +95,56 @@ def status_url(job_id, sub_path=""):
Impure
Pulls information from settings.

:arg uuid4 job_id: The UUID of the job.
:arg str sub_path: The part of the path indicating the type of job.

:rtype: str
:returns: The URL for checking on the status of the job.
:returns: The URL for host in the form host:port, for example:
http://domain.tld:2323

Note that the schema is not supplied (we assume it's included in the
string provided to settings) and no trailing slash is provided.

We assume that the port from setigns is the bare number, with no
trailing colon, so we add that here.
"""
hostname = getattr(settings, "CANONICAL_HOSTNAME", "")
hostport = getattr(settings, "CANONICAL_PORT", "")
if hostport:
if hostport and hostport is not "80":
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to do the same for tls + 443?

hostport = ":%s" % hostport
return "%s%s/rp/job/%s%s/" % (hostname, hostport, sub_path, job_id)
elif hostport is "80":
hostport = ""
return "%s%s" % (hostname, hostport)


def status_url(job_id, sub_path=""):
"""
Returns a URL for checking the status of a job.

Impure
Via get_host(), pulls information from settings.

:arg uuid4 job_id: The UUID of the job.
:arg str sub_path: The part of the path indicating the type of job. Must
include a trailing slash.

:rtype: str
:returns: The URL for checking on the status of the job.
"""
if sub_path and not sub_path.endswith("/"):
raise ValueError
host = get_host()
return "%s/rp/job/%s%s/" % (host, sub_path, job_id)


def file_url(file_hash):
"""
We want to give users a URL for retrieving an uploaded file.
While I can't think of an exploit resulting from relying on the host data
from the request if the request were spoofed, we'll be cautious and define
the canonical host data ourselves.
This helper function puts the lookup and the string manipulation in one
place.
Returns a URL for retrieving an uploaded file.

Impure
Pulls information from settings.
Via get_host(), pulls information from settings.

:arg str file_hash: The MD5 hexstring of the file contents.

:rtype: str
:returns: The URL for checking on the status of the job.
"""
hostname = getattr(settings, "CANONICAL_HOSTNAME", "")
hostport = getattr(settings, "CANONICAL_PORT", "")
if hostport:
hostport = ":%s" % hostport
return "%s%s/rp/job/upload/%s/" % (hostname, hostport, file_hash)
host = get_host()
return "%s/rp/job/upload/%s/" % (host, file_hash)
68 changes: 68 additions & 0 deletions tests/test_web_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from hashlib import md5
from mock import patch
from os import path as ospath
from random import choice
from regparser.web.jobs.models import job_status_values
from regparser.web.jobs.utils import (
eregs_site_api_url,
Expand All @@ -9,7 +10,12 @@
)
from regparser.web.jobs.views import FileUploadView as PatchedFileUploadView
from rest_framework.test import APITestCase
from string import hexdigits
from tempfile import NamedTemporaryFile

import pytest
import settings

try:
from urllib.parse import urlparse
except ImportError:
Expand All @@ -19,6 +25,10 @@
fake_email_id = "4774f0f6-b53e-4b34-821e-fc8ae5e113fe"
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to use uuid() instead? Using specific strings implies that these specific values are meaningful



def fake_getter(attrname):
return attrname


def fake_add_redis_data_to_job_data(job_data):
return job_data

Expand Down Expand Up @@ -305,3 +315,61 @@ def test_create_and_read_and_delete(self):

response = self.client.get("/rp/job/proposal-pipeline/", format="json")
self.assertEqual(0, len(response.data))


@patch.object(settings, "CANONICAL_HOSTNAME", "http://domain.tld")
def test_status_url():
domain = "http://domain.tld"
urlpath = "/rp/job/"
hexes = ["".join([choice(hexdigits) for i in range(32)]) for j in range(6)]

def _check(port=None):
for hx in hexes:
url = urlparse(status_url(hx))
assert domain == "%s://%s" % (url.scheme, url.hostname)
if port is None:
assert url.port is port
else:
assert url.port == port
assert "%s%s/" % (urlpath, hx) == url.path

url = urlparse(status_url(hx, sub_path="%s/" % hx[:10]))
assert domain == "%s://%s" % (url.scheme, url.hostname)
if port is None:
assert url.port is port
else:
assert url.port == port
assert "%s%s%s/" % (urlpath, "%s/" % hx[:10], hx) == url.path

with patch.object(settings, "CANONICAL_PORT", "2323"):
_check(port=2323)

with patch.object(settings, "CANONICAL_PORT", "80"):
_check()

with patch.object(settings, "CANONICAL_PORT", ""):
_check()

with pytest.raises(ValueError) as err:
status_url("something", "something-without-a-slash")

assert isinstance(err.value, ValueError)


@patch.object(settings, "CANONICAL_HOSTNAME", "http://domain.tld")
def test_file_url():
urlpath = "/rp/job/upload/"
domain = "http://domain.tld"
hexes = ["".join([choice(hexdigits) for i in range(32)]) for j in range(6)]

with patch.object(settings, "CANONICAL_PORT", "2323"):
for hx in hexes:
assert file_url(hx) == "%s:2323%s%s/" % (domain, urlpath, hx)

with patch.object(settings, "CANONICAL_PORT", "80"):
for hx in hexes:
assert file_url(hx) == "%s%s%s/" % (domain, urlpath, hx)

with patch.object(settings, "CANONICAL_PORT", ""):
for hx in hexes:
assert file_url(hx) == "%s%s%s/" % (domain, urlpath, hx)