diff --git a/hc/api/decorators.py b/hc/api/decorators.py new file mode 100644 index 00000000..f507d61e --- /dev/null +++ b/hc/api/decorators.py @@ -0,0 +1,15 @@ +from functools import wraps +from django.http import HttpResponseBadRequest +import uuid + + +def uuid_or_400(f): + @wraps(f) + def wrapper(request, *args, **kwds): + try: + uuid.UUID(args[0]) + except ValueError: + return HttpResponseBadRequest() + + return f(request, *args, **kwds) + return wrapper diff --git a/hc/api/tests/test_ping.py b/hc/api/tests/test_ping.py index 65bb1852..8a226db4 100644 --- a/hc/api/tests/test_ping.py +++ b/hc/api/tests/test_ping.py @@ -22,3 +22,7 @@ class PingTestCase(TestCase): csrf_client = Client(enforce_csrf_checks=True) r = csrf_client.post("/ping/%s/" % check.code) assert r.status_code == 200 + + def test_it_handles_bad_uuid(self): + r = self.client.get("/ping/not-uuid/") + assert r.status_code == 400 diff --git a/hc/api/tests/test_status.py b/hc/api/tests/test_status.py new file mode 100644 index 00000000..21292da6 --- /dev/null +++ b/hc/api/tests/test_status.py @@ -0,0 +1,17 @@ +from django.test import TestCase + +from hc.api.models import Check + + +class StatusTestCase(TestCase): + + def test_it_works(self): + check = Check() + check.save() + + r = self.client.get("/status/%s/" % check.code) + self.assertContains(r, "last_ping", status_code=200) + + def test_it_handles_bad_uuid(self): + r = self.client.get("/status/not-uuid/") + assert r.status_code == 400 diff --git a/hc/api/urls.py b/hc/api/urls.py index 3f688c92..ee1b829b 100644 --- a/hc/api/urls.py +++ b/hc/api/urls.py @@ -3,7 +3,7 @@ from django.conf.urls import url from hc.api import views urlpatterns = [ - url(r'^ping/([\w-]+)/$', views.ping, name="hc-ping"), + url(r'^ping/([\w-]+)/$', views.ping, name="hc-ping-slash"), url(r'^ping/([\w-]+)$', views.ping, name="hc-ping"), url(r'^status/([\w-]+)/$', views.status, name="hc-status"), ] diff --git a/hc/api/views.py b/hc/api/views.py index 811ab6a0..b481457a 100644 --- a/hc/api/views.py +++ b/hc/api/views.py @@ -5,10 +5,12 @@ from django.http import HttpResponse, HttpResponseBadRequest from django.utils import timezone from django.views.decorators.csrf import csrf_exempt +from hc.api.decorators import uuid_or_400 from hc.api.models import Check, Ping @csrf_exempt +@uuid_or_400 def ping(request, code): try: check = Check.objects.get(code=code) @@ -34,6 +36,7 @@ def ping(request, code): return response +@uuid_or_400 def status(request, code): response = { "last_ping": None, diff --git a/hc/front/tests/test_log.py b/hc/front/tests/test_log.py new file mode 100644 index 00000000..5d24a9bb --- /dev/null +++ b/hc/front/tests/test_log.py @@ -0,0 +1,29 @@ +from django.contrib.auth.models import User +from django.test import TestCase + +from hc.api.models import Check + + +class LogTestCase(TestCase): + + def setUp(self): + self.alice = User(username="alice") + self.alice.set_password("password") + self.alice.save() + + self.check = Check(user=self.alice) + self.check.save() + + def test_it_works(self): + url = "/checks/%s/log/" % self.check.code + + self.client.login(username="alice", password="password") + r = self.client.get(url) + self.assertContains(r, "Log for", status_code=200) + + def test_it_handles_bad_uuid(self): + url = "/checks/not-uuid/log/" + + self.client.login(username="alice", password="password") + r = self.client.get(url) + assert r.status_code == 400 diff --git a/hc/front/tests/test_remove.py b/hc/front/tests/test_remove.py new file mode 100644 index 00000000..76addb0a --- /dev/null +++ b/hc/front/tests/test_remove.py @@ -0,0 +1,31 @@ +from django.contrib.auth.models import User +from django.test import TestCase + +from hc.api.models import Check + + +class RemoveTestCase(TestCase): + + def setUp(self): + self.alice = User(username="alice") + self.alice.set_password("password") + self.alice.save() + + self.check = Check(user=self.alice) + self.check.save() + + def test_it_works(self): + url = "/checks/%s/remove/" % self.check.code + + self.client.login(username="alice", password="password") + r = self.client.post(url) + assert r.status_code == 302 + + assert Check.objects.count() == 0 + + def test_it_handles_bad_uuid(self): + url = "/checks/not-uuid/remove/" + + self.client.login(username="alice", password="password") + r = self.client.post(url) + assert r.status_code == 400 diff --git a/hc/front/tests/test_update_name.py b/hc/front/tests/test_update_name.py index f19289cc..02f10fd6 100644 --- a/hc/front/tests/test_update_name.py +++ b/hc/front/tests/test_update_name.py @@ -37,3 +37,11 @@ class UpdateNameTestCase(TestCase): self.client.login(username="charlie", password="password") r = self.client.post(url, data=payload) assert r.status_code == 403 + + def test_it_handles_bad_uuid(self): + url = "/checks/not-uuid/name/" + payload = {"name": "Alice Was Here"} + + self.client.login(username="alice", password="password") + r = self.client.post(url, data=payload) + assert r.status_code == 400 diff --git a/hc/front/tests/test_update_timeout.py b/hc/front/tests/test_update_timeout.py new file mode 100644 index 00000000..91f025df --- /dev/null +++ b/hc/front/tests/test_update_timeout.py @@ -0,0 +1,35 @@ +from django.contrib.auth.models import User +from django.test import TestCase + +from hc.api.models import Check + + +class UpdateTimeoutTestCase(TestCase): + + def setUp(self): + self.alice = User(username="alice") + self.alice.set_password("password") + self.alice.save() + + self.check = Check(user=self.alice) + self.check.save() + + def test_it_works(self): + url = "/checks/%s/timeout/" % self.check.code + payload = {"timeout": 3600, "grace": 60} + + self.client.login(username="alice", password="password") + r = self.client.post(url, data=payload) + assert r.status_code == 302 + + check = Check.objects.get(code=self.check.code) + assert check.timeout.total_seconds() == 3600 + assert check.grace.total_seconds() == 60 + + def test_it_handles_bad_uuid(self): + url = "/checks/not-uuid/timeout/" + payload = {"timeout": 3600, "grace": 60} + + self.client.login(username="alice", password="password") + r = self.client.post(url, data=payload) + assert r.status_code == 400 diff --git a/hc/front/views.py b/hc/front/views.py index 3cc190d6..3325756e 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -6,6 +6,7 @@ from django.http import HttpResponseForbidden from django.shortcuts import redirect, render from django.utils import timezone +from hc.api.decorators import uuid_or_400 from hc.api.models import Check, Ping from hc.front.forms import TimeoutForm @@ -84,6 +85,7 @@ def add_check(request): @login_required +@uuid_or_400 def update_name(request, code): assert request.method == "POST" @@ -98,6 +100,7 @@ def update_name(request, code): @login_required +@uuid_or_400 def update_timeout(request, code): assert request.method == "POST" @@ -115,6 +118,7 @@ def update_timeout(request, code): @login_required +@uuid_or_400 def email_preview(request, code): """ A debug view to see how email will look. @@ -137,6 +141,7 @@ def email_preview(request, code): @login_required +@uuid_or_400 def remove(request, code): assert request.method == "POST" @@ -150,6 +155,7 @@ def remove(request, code): @login_required +@uuid_or_400 def log(request, code): check = Check.objects.get(code=code)