From fdf9c607e52ff05811da5fe9c14c303f1298a6f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C4=93teris=20Caune?=
Date: Mon, 9 May 2016 15:35:13 +0300
Subject: [PATCH] Team Access, test cleanup
---
hc/accounts/admin.py | 14 +++--
hc/accounts/forms.py | 4 ++
.../management/commands/createprofiles.py | 3 +-
hc/accounts/middleware.py | 10 ++++
.../migrations/0006_profile_current_team.py | 21 +++++++
hc/accounts/models.py | 14 +----
hc/accounts/tests/test_check_token.py | 2 -
hc/accounts/tests/test_profile.py | 36 +++++++-----
hc/accounts/tests/test_switch_team.py | 37 ++++++++++++
hc/accounts/urls.py | 4 ++
hc/accounts/views.py | 46 +++++++++++----
hc/api/management/commands/prunepings.py | 2 +-
hc/api/management/commands/prunepingsslow.py | 2 +-
hc/api/management/commands/sendreports.py | 6 --
hc/api/tests/test_create_check.py | 2 -
hc/api/tests/test_list_checks.py | 8 +--
hc/front/tests/test_channel_checks.py | 8 +--
hc/front/tests/test_log.py | 6 --
hc/front/tests/test_remove_channel.py | 8 +--
hc/front/tests/test_remove_check.py | 8 +--
hc/front/tests/test_update_channel.py | 26 +++------
hc/front/tests/test_update_name.py | 7 ---
hc/front/tests/test_update_timeout.py | 6 --
hc/front/urls.py | 1 -
hc/front/views.py | 57 ++++++-------------
hc/payments/views.py | 2 +-
hc/settings.py | 1 +
hc/test.py | 15 +++++
templates/accounts/profile.html | 42 ++++++++++++++
templates/base.html | 19 ++++++-
30 files changed, 255 insertions(+), 162 deletions(-)
create mode 100644 hc/accounts/middleware.py
create mode 100644 hc/accounts/migrations/0006_profile_current_team.py
create mode 100644 hc/accounts/tests/test_switch_team.py
diff --git a/hc/accounts/admin.py b/hc/accounts/admin.py
index 1c019e5a..6985c154 100644
--- a/hc/accounts/admin.py
+++ b/hc/accounts/admin.py
@@ -1,6 +1,7 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
+from django.core.urlresolvers import reverse
from hc.accounts.models import Profile
from hc.api.models import Channel, Check
@@ -18,8 +19,8 @@ class ProfileAdmin(admin.ModelAdmin):
class HcUserAdmin(UserAdmin):
actions = ["send_report"]
- list_display = ('id', 'username', 'email', 'date_joined', 'involvement',
- 'is_staff')
+ list_display = ('id', 'email', 'date_joined', 'involvement',
+ 'is_staff', 'checks')
ordering = ["-id"]
@@ -46,10 +47,15 @@ class HcUserAdmin(UserAdmin):
involvement.allow_tags = True
+ def checks(self, user):
+ url = reverse("hc-switch-team", args=[user.username])
+ return "Checks" % url
+
+ checks.allow_tags = True
+
def send_report(self, request, qs):
for user in qs:
- profile = Profile.objects.for_user(user)
- profile.send_report()
+ user.profile.send_report()
self.message_user(request, "%d email(s) sent" % qs.count())
diff --git a/hc/accounts/forms.py b/hc/accounts/forms.py
index 48aeabfb..544cc3b3 100644
--- a/hc/accounts/forms.py
+++ b/hc/accounts/forms.py
@@ -27,3 +27,7 @@ class InviteTeamMemberForm(forms.Form):
class RemoveTeamMemberForm(forms.Form):
email = LowercaseEmailField()
+
+
+class TeamNameForm(forms.Form):
+ team_name = forms.CharField(max_length=200, required=True)
diff --git a/hc/accounts/management/commands/createprofiles.py b/hc/accounts/management/commands/createprofiles.py
index c1fbf7a8..23dc795d 100644
--- a/hc/accounts/management/commands/createprofiles.py
+++ b/hc/accounts/management/commands/createprofiles.py
@@ -8,7 +8,6 @@ class Command(BaseCommand):
def handle(self, *args, **options):
for user in User.objects.all():
- # this should create profile object if it does not exist
- Profile.objects.for_user(user)
+ Profile.objects.get_or_create(user_id=user.id)
print("Done.")
diff --git a/hc/accounts/middleware.py b/hc/accounts/middleware.py
new file mode 100644
index 00000000..bba200fb
--- /dev/null
+++ b/hc/accounts/middleware.py
@@ -0,0 +1,10 @@
+class TeamAccessMiddleware(object):
+ def process_request(self, request):
+ if not request.user.is_authenticated():
+ return
+
+ profile = request.user.profile
+ if profile.current_team:
+ request.team = profile.current_team
+ else:
+ request.team = profile
diff --git a/hc/accounts/migrations/0006_profile_current_team.py b/hc/accounts/migrations/0006_profile_current_team.py
new file mode 100644
index 00000000..2d93e974
--- /dev/null
+++ b/hc/accounts/migrations/0006_profile_current_team.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9 on 2016-05-09 10:34
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('accounts', '0005_auto_20160509_0801'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='profile',
+ name='current_team',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='accounts.Profile'),
+ ),
+ ]
diff --git a/hc/accounts/models.py b/hc/accounts/models.py
index 9a229314..d2283ac7 100644
--- a/hc/accounts/models.py
+++ b/hc/accounts/models.py
@@ -13,27 +13,17 @@ from django.utils import timezone
from hc.lib import emails
-class ProfileManager(models.Manager):
-
- def for_user(self, user):
- profile, created = Profile.objects.get_or_create(user_id=user.id)
- return profile
-
-
class Profile(models.Model):
# Owner:
user = models.OneToOneField(User, blank=True, null=True)
-
team_name = models.CharField(max_length=200, blank=True)
team_access_allowed = models.BooleanField(default=False)
-
next_report_date = models.DateTimeField(null=True, blank=True)
reports_allowed = models.BooleanField(default=True)
ping_log_limit = models.IntegerField(default=100)
token = models.CharField(max_length=128, blank=True)
api_key = models.CharField(max_length=128, blank=True)
-
- objects = ProfileManager()
+ current_team = models.ForeignKey("self", null=True)
def __str__(self):
return self.team_name or self.user.email
@@ -85,7 +75,7 @@ class Profile(models.Model):
member = Member(team=self, user=user)
member.save()
- Profile.objects.for_user(user).send_instant_login_link(self)
+ user.profile.send_instant_login_link(self)
class Member(models.Model):
diff --git a/hc/accounts/tests/test_check_token.py b/hc/accounts/tests/test_check_token.py
index 51ca036b..d1a33112 100644
--- a/hc/accounts/tests/test_check_token.py
+++ b/hc/accounts/tests/test_check_token.py
@@ -1,5 +1,4 @@
from django.contrib.auth.hashers import make_password
-from hc.accounts.models import Profile
from hc.test import BaseTestCase
@@ -7,7 +6,6 @@ class CheckTokenTestCase(BaseTestCase):
def setUp(self):
super(CheckTokenTestCase, self).setUp()
- self.profile = Profile(user=self.alice)
self.profile.token = make_password("secret-token")
self.profile.save()
diff --git a/hc/accounts/tests/test_profile.py b/hc/accounts/tests/test_profile.py
index 04441461..97cabc55 100644
--- a/hc/accounts/tests/test_profile.py
+++ b/hc/accounts/tests/test_profile.py
@@ -2,11 +2,11 @@ from django.contrib.auth.models import User
from django.core import mail
from hc.test import BaseTestCase
-from hc.accounts.models import Profile, Member
+from hc.accounts.models import Member
from hc.api.models import Check
-class LoginTestCase(BaseTestCase):
+class ProfileTestCase(BaseTestCase):
def test_it_sends_set_password_link(self):
self.client.login(username="alice@example.org", password="password")
@@ -16,8 +16,9 @@ class LoginTestCase(BaseTestCase):
assert r.status_code == 302
# profile.token should be set now
- profile = Profile.objects.for_user(self.alice)
- self.assertTrue(len(profile.token) > 10)
+ self.alice.profile.refresh_from_db()
+ token = self.alice.profile.token
+ self.assertTrue(len(token) > 10)
# And an email should have been sent
self.assertEqual(len(mail.outbox), 1)
@@ -31,8 +32,9 @@ class LoginTestCase(BaseTestCase):
r = self.client.post("/accounts/profile/", form)
assert r.status_code == 200
- profile = Profile.objects.for_user(self.alice)
- self.assertTrue(len(profile.api_key) > 10)
+ self.alice.profile.refresh_from_db()
+ api_key = self.alice.profile.api_key
+ self.assertTrue(len(api_key) > 10)
def test_it_revokes_api_key(self):
self.client.login(username="alice@example.org", password="password")
@@ -41,15 +43,14 @@ class LoginTestCase(BaseTestCase):
r = self.client.post("/accounts/profile/", form)
assert r.status_code == 200
- profile = Profile.objects.for_user(self.alice)
- self.assertEqual(profile.api_key, "")
+ self.alice.profile.refresh_from_db()
+ self.assertEqual(self.alice.profile.api_key, "")
def test_it_sends_report(self):
check = Check(name="Test Check", user=self.alice)
check.save()
- profile = Profile.objects.for_user(self.alice)
- profile.send_report()
+ self.alice.profile.send_report()
# And an email should have been sent
self.assertEqual(len(mail.outbox), 1)
@@ -65,8 +66,7 @@ class LoginTestCase(BaseTestCase):
r = self.client.post("/accounts/profile/", form)
assert r.status_code == 200
- profile = Profile.objects.for_user(self.alice)
- member = profile.member_set.get()
+ member = self.alice.profile.member_set.get()
self.assertEqual(member.user.email, "bob@example.org")
@@ -81,7 +81,7 @@ class LoginTestCase(BaseTestCase):
bob = User(username="bob", email="bob@example.org")
bob.save()
- m = Member(team=Profile.objects.for_user(self.alice), user=bob)
+ m = Member(team=self.alice.profile, user=bob)
m.save()
form = {"remove_team_member": "1", "email": "bob@example.org"}
@@ -89,3 +89,13 @@ class LoginTestCase(BaseTestCase):
assert r.status_code == 200
self.assertEqual(Member.objects.count(), 0)
+
+ def test_it_sets_team_name(self):
+ self.client.login(username="alice@example.org", password="password")
+
+ form = {"set_team_name": "1", "team_name": "Alpha Team"}
+ r = self.client.post("/accounts/profile/", form)
+ assert r.status_code == 200
+
+ self.alice.profile.refresh_from_db()
+ self.assertEqual(self.alice.profile.team_name, "Alpha Team")
diff --git a/hc/accounts/tests/test_switch_team.py b/hc/accounts/tests/test_switch_team.py
new file mode 100644
index 00000000..c9e37f39
--- /dev/null
+++ b/hc/accounts/tests/test_switch_team.py
@@ -0,0 +1,37 @@
+from django.contrib.auth.models import User
+
+from hc.test import BaseTestCase
+from hc.accounts.models import Member, Profile
+
+
+class SwitchTeamTestCase(BaseTestCase):
+
+ def setUp(self):
+ super(SwitchTeamTestCase, self).setUp()
+
+ self.bob = User(username="bob", email="bob@example.org")
+ self.bob.set_password("password")
+ self.bob.save()
+
+ bobs_profile = Profile(user=self.bob)
+ bobs_profile.save()
+
+
+ m = Member(team=bobs_profile, user=self.alice)
+ m.save()
+
+ def test_it_switches(self):
+ self.client.login(username="alice@example.org", password="password")
+
+ url = "/accounts/switch_team/%s/" % self.bob.username
+ r = self.client.get(url, follow=True)
+
+ self.assertContains(r, "bob@example.org")
+
+
+ def test_it_checks_team_membership(self):
+ self.client.login(username="charlie@example.org", password="password")
+
+ url = "/accounts/switch_team/%s/" % self.bob.username
+ r = self.client.get(url)
+ self.assertEqual(r.status_code, 403)
diff --git a/hc/accounts/urls.py b/hc/accounts/urls.py
index 333a0c8f..452c4c09 100644
--- a/hc/accounts/urls.py
+++ b/hc/accounts/urls.py
@@ -21,4 +21,8 @@ urlpatterns = [
url(r'^set_password/([\w-]+)/$',
views.set_password, name="hc-set-password"),
+ url(r'^switch_team/([\w-]+)/$',
+ views.switch_team, name="hc-switch-team"),
+
+
]
diff --git a/hc/accounts/views.py b/hc/accounts/views.py
index 2efbfdc6..5078eac6 100644
--- a/hc/accounts/views.py
+++ b/hc/accounts/views.py
@@ -8,11 +8,11 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User
from django.core import signing
-from django.http import HttpResponseBadRequest
+from django.http import HttpResponseForbidden, HttpResponseBadRequest
from django.shortcuts import redirect, render
from hc.accounts.forms import (EmailPasswordForm, InviteTeamMemberForm,
RemoveTeamMemberForm, ReportSettingsForm,
- SetPasswordForm)
+ SetPasswordForm, TeamNameForm)
from hc.accounts.models import Profile, Member
from hc.api.models import Channel, Check
@@ -23,6 +23,9 @@ def _make_user(email):
user.set_unusable_password()
user.save()
+ profile = Profile(user=user)
+ profile.save()
+
channel = Channel()
channel.user = user
channel.kind = "email"
@@ -67,8 +70,7 @@ def login(request):
user = _make_user(email)
_associate_demo_check(request, user)
- profile = Profile.objects.for_user(user)
- profile.send_instant_login_link()
+ user.profile.send_instant_login_link()
return redirect("hc-login-link-sent")
else:
@@ -106,9 +108,8 @@ def check_token(request, username, token):
# This should get rid of "welcome_code" in session
request.session.flush()
- profile = Profile.objects.for_user(user)
- profile.token = ""
- profile.save()
+ user.profile.token = ""
+ user.profile.save()
auth_login(request, user)
return redirect("hc-checks")
@@ -119,7 +120,7 @@ def check_token(request, username, token):
@login_required
def profile(request):
- profile = Profile.objects.for_user(request.user)
+ profile = request.user.profile
show_api_key = False
if request.method == "POST":
@@ -161,6 +162,12 @@ def profile(request):
email = form.cleaned_data["email"]
Member.objects.filter(team=profile, user__email=email).delete()
messages.info(request, "%s removed from team!" % email)
+ elif "set_team_name" in request.POST:
+ form = TeamNameForm(request.POST)
+ if form.is_valid():
+ profile.team_name = form.cleaned_data["team_name"]
+ profile.save()
+ messages.info(request, "Team Name updated!")
ctx = {
"profile": profile,
@@ -172,7 +179,7 @@ def profile(request):
@login_required
def set_password(request, token):
- profile = Profile.objects.for_user(request.user)
+ profile = request.user.profile
if not check_password(token, profile.token):
return HttpResponseBadRequest()
@@ -204,8 +211,23 @@ def unsubscribe_reports(request, username):
return HttpResponseBadRequest()
user = User.objects.get(username=username)
- profile = Profile.objects.for_user(user)
- profile.reports_allowed = False
- profile.save()
+ user.profile.reports_allowed = False
+ user.profile.save()
return render(request, "accounts/unsubscribed.html")
+
+
+def switch_team(request, target_username):
+ other_user = User.objects.get(username=target_username)
+
+ # Superuser can switch to any team.
+ # Other users can only switch to a team they are members of.
+ if not request.user.is_superuser:
+ q = Member.objects.filter(team=other_user.profile, user=request.user)
+ if q.count() == 0:
+ return HttpResponseForbidden()
+
+ request.user.profile.current_team = other_user.profile
+ request.user.profile.save()
+
+ return redirect("hc-checks")
diff --git a/hc/api/management/commands/prunepings.py b/hc/api/management/commands/prunepings.py
index 0050d6e8..7a233001 100644
--- a/hc/api/management/commands/prunepings.py
+++ b/hc/api/management/commands/prunepings.py
@@ -11,7 +11,7 @@ class Command(BaseCommand):
def handle(self, *args, **options):
# Create any missing user profiles
for user in User.objects.filter(profile=None):
- Profile.objects.for_user(user)
+ Profile.objects.get_or_create(user_id=user.id)
q = Ping.objects
q = q.annotate(limit=F("owner__user__profile__ping_log_limit"))
diff --git a/hc/api/management/commands/prunepingsslow.py b/hc/api/management/commands/prunepingsslow.py
index 56b0bcdd..1ceabf85 100644
--- a/hc/api/management/commands/prunepingsslow.py
+++ b/hc/api/management/commands/prunepingsslow.py
@@ -18,7 +18,7 @@ class Command(BaseCommand):
def handle(self, *args, **options):
# Create any missing user profiles
for user in User.objects.filter(profile=None):
- Profile.objects.for_user(user)
+ Profile.objects.get_or_create(user_id=user.id)
checks = Check.objects.filter(user__isnull=False)
checks = checks.annotate(limit=F("user__profile__ping_log_limit"))
diff --git a/hc/api/management/commands/sendreports.py b/hc/api/management/commands/sendreports.py
index 9ef55eb2..47afb724 100644
--- a/hc/api/management/commands/sendreports.py
+++ b/hc/api/management/commands/sendreports.py
@@ -1,6 +1,5 @@
from datetime import timedelta
-from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.db.models import Q
from django.utils import timezone
@@ -19,11 +18,6 @@ class Command(BaseCommand):
tmpl = "Sending monthly report to %s"
def handle(self, *args, **options):
- # Create any missing profiles
- for u in User.objects.filter(profile__isnull=True):
- self.stdout.write("Creating profile for %s" % u.email)
- Profile.objects.for_user(u)
-
now = timezone.now()
month_before = now - timedelta(days=30)
diff --git a/hc/api/tests/test_create_check.py b/hc/api/tests/test_create_check.py
index 52d3084f..a18a639a 100644
--- a/hc/api/tests/test_create_check.py
+++ b/hc/api/tests/test_create_check.py
@@ -9,8 +9,6 @@ class CreateCheckTestCase(BaseTestCase):
def setUp(self):
super(CreateCheckTestCase, self).setUp()
- self.profile = Profile(user=self.alice, api_key="abc")
- self.profile.save()
def post(self, url, data):
return self.client.post(url, json.dumps(data),
diff --git a/hc/api/tests/test_list_checks.py b/hc/api/tests/test_list_checks.py
index 15a36e62..2ee8563b 100644
--- a/hc/api/tests/test_list_checks.py
+++ b/hc/api/tests/test_list_checks.py
@@ -3,14 +3,13 @@ from datetime import timedelta as td
from hc.api.models import Check, User
from hc.test import BaseTestCase
-from hc.accounts.models import Profile
+
class ListChecksTestCase(BaseTestCase):
def setUp(self):
super(ListChecksTestCase, self).setUp()
- self.profile = Profile(user=self.alice, api_key="abc")
- self.profile.save()
+
self.checks = [
Check(user=self.alice, name="Alice 1", timeout=td(seconds=3600), grace=td(seconds=900)),
Check(user=self.alice, name="Alice 2", timeout=td(seconds=86400), grace=td(seconds=3600)),
@@ -40,8 +39,9 @@ class ListChecksTestCase(BaseTestCase):
bob = User(username="bob", email="bob@example.com")
bob.save()
bob_check = Check(user=bob, name="Bob 1")
+ bob_check.save()
- r = self.get("/api/v1/checks/", { "api_key": "abc" })
+ r = self.get("/api/v1/checks/", {"api_key": "abc"})
self.assertEqual(len(r.json()["checks"]), 2)
checks = { check["name"]: check for check in r.json()["checks"] }
diff --git a/hc/front/tests/test_channel_checks.py b/hc/front/tests/test_channel_checks.py
index adfc082c..1f6c8672 100644
--- a/hc/front/tests/test_channel_checks.py
+++ b/hc/front/tests/test_channel_checks.py
@@ -1,5 +1,3 @@
-from django.contrib.auth.models import User
-
from hc.api.models import Channel
from hc.test import BaseTestCase
@@ -20,14 +18,10 @@ class ChannelChecksTestCase(BaseTestCase):
self.assertContains(r, "Assign Checks to Channel", status_code=200)
def test_it_checks_owner(self):
- mallory = User(username="mallory", email="mallory@example.org")
- mallory.set_password("password")
- mallory.save()
-
# channel does not belong to mallory so this should come back
# with 403 Forbidden:
url = "/integrations/%s/checks/" % self.channel.code
- self.client.login(username="mallory@example.org", password="password")
+ self.client.login(username="charlie@example.org", password="password")
r = self.client.get(url)
assert r.status_code == 403
diff --git a/hc/front/tests/test_log.py b/hc/front/tests/test_log.py
index d4f420be..c39d4145 100644
--- a/hc/front/tests/test_log.py
+++ b/hc/front/tests/test_log.py
@@ -1,5 +1,3 @@
-from django.contrib.auth.models import User
-
from hc.api.models import Check, Ping
from hc.test import BaseTestCase
@@ -37,10 +35,6 @@ class LogTestCase(BaseTestCase):
assert r.status_code == 404
def test_it_checks_ownership(self):
- charlie = User(username="charlie", email="charlie@example.org")
- charlie.set_password("password")
- charlie.save()
-
url = "/checks/%s/log/" % self.check.code
self.client.login(username="charlie@example.org", password="password")
r = self.client.get(url)
diff --git a/hc/front/tests/test_remove_channel.py b/hc/front/tests/test_remove_channel.py
index d6a94fd2..aa083b43 100644
--- a/hc/front/tests/test_remove_channel.py
+++ b/hc/front/tests/test_remove_channel.py
@@ -1,5 +1,3 @@
-from django.contrib.auth.models import User
-
from hc.api.models import Channel
from hc.test import BaseTestCase
@@ -31,11 +29,7 @@ class RemoveChannelTestCase(BaseTestCase):
def test_it_checks_owner(self):
url = "/integrations/%s/remove/" % self.channel.code
- mallory = User(username="mallory", email="mallory@example.org")
- mallory.set_password("password")
- mallory.save()
-
- self.client.login(username="mallory@example.org", password="password")
+ self.client.login(username="charlie@example.org", password="password")
r = self.client.post(url)
assert r.status_code == 403
diff --git a/hc/front/tests/test_remove_check.py b/hc/front/tests/test_remove_check.py
index 10dc73aa..c0d5277e 100644
--- a/hc/front/tests/test_remove_check.py
+++ b/hc/front/tests/test_remove_check.py
@@ -1,5 +1,3 @@
-from django.contrib.auth.models import User
-
from hc.api.models import Check
from hc.test import BaseTestCase
@@ -30,11 +28,7 @@ class RemoveCheckTestCase(BaseTestCase):
def test_it_checks_owner(self):
url = "/checks/%s/remove/" % self.check.code
- mallory = User(username="mallory", email="mallory@example.org")
- mallory.set_password("password")
- mallory.save()
-
- self.client.login(username="mallory@example.org", password="password")
+ self.client.login(username="charlie@example.org", password="password")
r = self.client.post(url)
assert r.status_code == 403
diff --git a/hc/front/tests/test_update_channel.py b/hc/front/tests/test_update_channel.py
index d5900385..6eeb5108 100644
--- a/hc/front/tests/test_update_channel.py
+++ b/hc/front/tests/test_update_channel.py
@@ -1,5 +1,3 @@
-from django.contrib.auth.models import User
-
from hc.api.models import Channel, Check
from hc.test import BaseTestCase
@@ -31,35 +29,27 @@ class UpdateChannelTestCase(BaseTestCase):
assert checks[0].code == self.check.code
def test_it_checks_channel_user(self):
- mallory = User(username="mallory", email="mallory@example.org")
- mallory.set_password("password")
- mallory.save()
-
payload = {"channel": self.channel.code}
- self.client.login(username="mallory@example.org", password="password")
+ self.client.login(username="charlie@example.org", password="password")
r = self.client.post("/integrations/", data=payload)
- # self.channel does not belong to mallory, this should fail--
+ # self.channel does not belong to charlie, this should fail--
assert r.status_code == 403
def test_it_checks_check_user(self):
- mallory = User(username="mallory", email="mallory@example.org")
- mallory.set_password("password")
- mallory.save()
-
- mc = Channel(user=mallory, kind="email")
- mc.email = "mallory@example.org"
- mc.save()
+ charlies_channel = Channel(user=self.charlie, kind="email")
+ charlies_channel.email = "charlie@example.org"
+ charlies_channel.save()
payload = {
- "channel": mc.code,
+ "channel": charlies_channel.code,
"check-%s" % self.check.code: True
}
- self.client.login(username="mallory@example.org", password="password")
+ self.client.login(username="charlie@example.org", password="password")
r = self.client.post("/integrations/", data=payload)
- # mc belongs to mallorym but self.check does not--
+ # mc belongs to charlie but self.check does not--
assert r.status_code == 403
def test_it_handles_missing_channel(self):
diff --git a/hc/front/tests/test_update_name.py b/hc/front/tests/test_update_name.py
index 9ef8327d..78035468 100644
--- a/hc/front/tests/test_update_name.py
+++ b/hc/front/tests/test_update_name.py
@@ -1,5 +1,3 @@
-from django.contrib.auth.models import User
-
from hc.api.models import Check
from hc.test import BaseTestCase
@@ -23,11 +21,6 @@ class UpdateNameTestCase(BaseTestCase):
assert check.name == "Alice Was Here"
def test_it_checks_ownership(self):
-
- charlie = User(username="charlie", email="charlie@example.org")
- charlie.set_password("password")
- charlie.save()
-
url = "/checks/%s/name/" % self.check.code
payload = {"name": "Charlie Sent This"}
diff --git a/hc/front/tests/test_update_timeout.py b/hc/front/tests/test_update_timeout.py
index 10c5acbd..5daeb213 100644
--- a/hc/front/tests/test_update_timeout.py
+++ b/hc/front/tests/test_update_timeout.py
@@ -1,5 +1,3 @@
-from django.contrib.auth.models import User
-
from hc.api.models import Check
from hc.test import BaseTestCase
@@ -41,10 +39,6 @@ class UpdateTimeoutTestCase(BaseTestCase):
assert r.status_code == 404
def test_it_checks_ownership(self):
- charlie = User(username="charlie", email="charlie@example.org")
- charlie.set_password("password")
- charlie.save()
-
url = "/checks/%s/timeout/" % self.check.code
payload = {"timeout": 3600, "grace": 60}
diff --git a/hc/front/urls.py b/hc/front/urls.py
index fae8a337..18744795 100644
--- a/hc/front/urls.py
+++ b/hc/front/urls.py
@@ -8,7 +8,6 @@ urlpatterns = [
url(r'^checks/add/$', views.add_check, name="hc-add-check"),
url(r'^checks/([\w-]+)/name/$', views.update_name, name="hc-update-name"),
url(r'^checks/([\w-]+)/timeout/$', views.update_timeout, name="hc-update-timeout"),
- url(r'^checks/([\w-]+)/email/$', views.email_preview),
url(r'^checks/([\w-]+)/remove/$', views.remove_check, name="hc-remove-check"),
url(r'^checks/([\w-]+)/log/$', views.log, name="hc-log"),
url(r'^docs/$', views.docs, name="hc-docs"),
diff --git a/hc/front/views.py b/hc/front/views.py
index 9ce1d9ff..e5ac8791 100644
--- a/hc/front/views.py
+++ b/hc/front/views.py
@@ -11,7 +11,6 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.six.moves.urllib.parse import urlencode
-from hc.accounts.models import Profile
from hc.api.decorators import uuid_or_400
from hc.api.models import Channel, Check, Ping, DEFAULT_TIMEOUT, DEFAULT_GRACE
from hc.front.forms import (AddChannelForm, AddWebhookForm, NameTagsForm,
@@ -28,7 +27,7 @@ def pairwise(iterable):
@login_required
def my_checks(request):
- checks = Check.objects.filter(user=request.user).order_by("created")
+ checks = Check.objects.filter(user=request.team.user).order_by("created")
counter = Counter()
down_tags, grace_tags = set(), set()
@@ -122,7 +121,7 @@ def about(request):
def add_check(request):
assert request.method == "POST"
- check = Check(user=request.user)
+ check = Check(user=request.team.user)
check.save()
check.assign_all_channels()
@@ -136,7 +135,7 @@ def update_name(request, code):
assert request.method == "POST"
check = get_object_or_404(Check, code=code)
- if check.user_id != request.user.id:
+ if check.user_id != request.team.user.id:
return HttpResponseForbidden()
form = NameTagsForm(request.POST)
@@ -154,7 +153,7 @@ def update_timeout(request, code):
assert request.method == "POST"
check = get_object_or_404(Check, code=code)
- if check.user != request.user:
+ if check.user != request.team.user:
return HttpResponseForbidden()
form = TimeoutForm(request.POST)
@@ -166,36 +165,13 @@ def update_timeout(request, code):
return redirect("hc-checks")
-@login_required
-@uuid_or_400
-def email_preview(request, code):
- """ A debug view to see how email will look.
-
- Will keep it around until I'm happy with email stying.
-
- """
-
- check = Check.objects.get(code=code)
- if check.user != request.user:
- return HttpResponseForbidden()
-
- ctx = {
- "check": check,
- "checks": check.user.check_set.all(),
- "now": timezone.now()
-
- }
-
- return render(request, "emails/alert/body.html", ctx)
-
-
@login_required
@uuid_or_400
def remove_check(request, code):
assert request.method == "POST"
check = get_object_or_404(Check, code=code)
- if check.user != request.user:
+ if check.user != request.team.user:
return HttpResponseForbidden()
check.delete()
@@ -207,11 +183,10 @@ def remove_check(request, code):
@uuid_or_400
def log(request, code):
check = get_object_or_404(Check, code=code)
- if check.user != request.user:
+ if check.user != request.team.user:
return HttpResponseForbidden()
- profile = Profile.objects.for_user(request.user)
- limit = profile.ping_log_limit
+ limit = request.team.ping_log_limit
pings = Ping.objects.filter(owner=check).order_by("-id")[:limit]
pings = list(pings.iterator())
@@ -264,7 +239,7 @@ def channels(request):
channel = Channel.objects.get(code=code)
except Channel.DoesNotExist:
return HttpResponseBadRequest()
- if channel.user_id != request.user.id:
+ if channel.user_id != request.team.user.id:
return HttpResponseForbidden()
new_checks = []
@@ -275,17 +250,17 @@ def channels(request):
check = Check.objects.get(code=code)
except Check.DoesNotExist:
return HttpResponseBadRequest()
- if check.user_id != request.user.id:
+ if check.user_id != request.team.user.id:
return HttpResponseForbidden()
new_checks.append(check)
channel.checks = new_checks
return redirect("hc-channels")
- channels = Channel.objects.filter(user=request.user).order_by("created")
+ channels = Channel.objects.filter(user=request.team.user).order_by("created")
channels = channels.annotate(n_checks=Count("checks"))
- num_checks = Check.objects.filter(user=request.user).count()
+ num_checks = Check.objects.filter(user=request.team.user).count()
ctx = {
"page": "channels",
@@ -300,7 +275,7 @@ def do_add_channel(request, data):
form = AddChannelForm(data)
if form.is_valid():
channel = form.save(commit=False)
- channel.user = request.user
+ channel.user = request.team.user
channel.save()
channel.assign_all_checks()
@@ -323,11 +298,11 @@ def add_channel(request):
@uuid_or_400
def channel_checks(request, code):
channel = get_object_or_404(Channel, code=code)
- if channel.user_id != request.user.id:
+ if channel.user_id != request.team.user.id:
return HttpResponseForbidden()
assigned = set(channel.checks.values_list('code', flat=True).distinct())
- checks = Check.objects.filter(user=request.user).order_by("created")
+ checks = Check.objects.filter(user=request.team.user).order_by("created")
ctx = {
"checks": checks,
@@ -357,7 +332,7 @@ def remove_channel(request, code):
# user may refresh the page during POST and cause two deletion attempts
channel = Channel.objects.filter(code=code).first()
if channel:
- if channel.user != request.user:
+ if channel.user != request.team.user:
return HttpResponseForbidden()
channel.delete()
@@ -375,7 +350,7 @@ def add_webhook(request):
if request.method == "POST":
form = AddWebhookForm(request.POST)
if form.is_valid():
- channel = Channel(user=request.user, kind="webhook")
+ channel = Channel(user=request.team.user, kind="webhook")
channel.value = form.get_value()
channel.save()
diff --git a/hc/payments/views.py b/hc/payments/views.py
index d9f8a4b6..b80aaa2c 100644
--- a/hc/payments/views.py
+++ b/hc/payments/views.py
@@ -106,7 +106,7 @@ def create_plan(request):
sub.save()
# Update user's profile
- profile = Profile.objects.for_user(request.user)
+ profile = request.user.profile
if plan_id == "P5":
profile.ping_log_limit = 1000
profile.team_access_allowed = True
diff --git a/hc/settings.py b/hc/settings.py
index feeec279..c4f5b928 100644
--- a/hc/settings.py
+++ b/hc/settings.py
@@ -49,6 +49,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
+ 'hc.accounts.middleware.TeamAccessMiddleware',
)
AUTHENTICATION_BACKENDS = (
diff --git a/hc/test.py b/hc/test.py
index 9fdb1495..2b1ee979 100644
--- a/hc/test.py
+++ b/hc/test.py
@@ -1,11 +1,26 @@
from django.contrib.auth.models import User
from django.test import TestCase
+from hc.accounts.models import Profile
+
class BaseTestCase(TestCase):
def setUp(self):
super(BaseTestCase, self).setUp()
+
+ # Normal user for tests
self.alice = User(username="alice", email="alice@example.org")
self.alice.set_password("password")
self.alice.save()
+
+ self.profile = Profile(user=self.alice, api_key="abc")
+ self.profile.save()
+
+ # "malicious user for tests
+ self.charlie = User(username="charlie", email="charlie@example.org")
+ self.charlie.set_password("password")
+ self.charlie.save()
+
+ charlies_profile = Profile(user=self.charlie)
+ charlies_profile.save()
diff --git a/templates/accounts/profile.html b/templates/accounts/profile.html
index 194daaa1..a584fec2 100644
--- a/templates/accounts/profile.html
+++ b/templates/accounts/profile.html
@@ -131,6 +131,14 @@
{% endif %}
+
+
+ Set Team Name
+
+
+
{% endblock %}
{% block scripts %}
diff --git a/templates/base.html b/templates/base.html
index 08d5b5ad..f6c5f1d0 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -110,11 +110,26 @@