Browse Source

Team Access, test cleanup

pull/60/head
Pēteris Caune 9 years ago
parent
commit
fdf9c607e5
30 changed files with 255 additions and 162 deletions
  1. +10
    -4
      hc/accounts/admin.py
  2. +4
    -0
      hc/accounts/forms.py
  3. +1
    -2
      hc/accounts/management/commands/createprofiles.py
  4. +10
    -0
      hc/accounts/middleware.py
  5. +21
    -0
      hc/accounts/migrations/0006_profile_current_team.py
  6. +2
    -12
      hc/accounts/models.py
  7. +0
    -2
      hc/accounts/tests/test_check_token.py
  8. +23
    -13
      hc/accounts/tests/test_profile.py
  9. +37
    -0
      hc/accounts/tests/test_switch_team.py
  10. +4
    -0
      hc/accounts/urls.py
  11. +34
    -12
      hc/accounts/views.py
  12. +1
    -1
      hc/api/management/commands/prunepings.py
  13. +1
    -1
      hc/api/management/commands/prunepingsslow.py
  14. +0
    -6
      hc/api/management/commands/sendreports.py
  15. +0
    -2
      hc/api/tests/test_create_check.py
  16. +4
    -4
      hc/api/tests/test_list_checks.py
  17. +1
    -7
      hc/front/tests/test_channel_checks.py
  18. +0
    -6
      hc/front/tests/test_log.py
  19. +1
    -7
      hc/front/tests/test_remove_channel.py
  20. +1
    -7
      hc/front/tests/test_remove_check.py
  21. +8
    -18
      hc/front/tests/test_update_channel.py
  22. +0
    -7
      hc/front/tests/test_update_name.py
  23. +0
    -6
      hc/front/tests/test_update_timeout.py
  24. +0
    -1
      hc/front/urls.py
  25. +16
    -41
      hc/front/views.py
  26. +1
    -1
      hc/payments/views.py
  27. +1
    -0
      hc/settings.py
  28. +15
    -0
      hc/test.py
  29. +42
    -0
      templates/accounts/profile.html
  30. +17
    -2
      templates/base.html

+ 10
- 4
hc/accounts/admin.py View File

@ -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 "<a href='%s'>Checks</a>" % 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())


+ 4
- 0
hc/accounts/forms.py View File

@ -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)

+ 1
- 2
hc/accounts/management/commands/createprofiles.py View File

@ -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.")

+ 10
- 0
hc/accounts/middleware.py View File

@ -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

+ 21
- 0
hc/accounts/migrations/0006_profile_current_team.py View File

@ -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'),
),
]

+ 2
- 12
hc/accounts/models.py View File

@ -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):


+ 0
- 2
hc/accounts/tests/test_check_token.py View File

@ -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()


+ 23
- 13
hc/accounts/tests/test_profile.py View File

@ -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="[email protected]", 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="[email protected]", 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, "[email protected]")
@ -81,7 +81,7 @@ class LoginTestCase(BaseTestCase):
bob = User(username="bob", email="[email protected]")
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": "[email protected]"}
@ -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="[email protected]", 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")

+ 37
- 0
hc/accounts/tests/test_switch_team.py View File

@ -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="[email protected]")
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="[email protected]", password="password")
url = "/accounts/switch_team/%s/" % self.bob.username
r = self.client.get(url, follow=True)
self.assertContains(r, "[email protected]")
def test_it_checks_team_membership(self):
self.client.login(username="[email protected]", password="password")
url = "/accounts/switch_team/%s/" % self.bob.username
r = self.client.get(url)
self.assertEqual(r.status_code, 403)

+ 4
- 0
hc/accounts/urls.py View File

@ -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"),
]

+ 34
- 12
hc/accounts/views.py View File

@ -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")

+ 1
- 1
hc/api/management/commands/prunepings.py View File

@ -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"))


+ 1
- 1
hc/api/management/commands/prunepingsslow.py View File

@ -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"))


+ 0
- 6
hc/api/management/commands/sendreports.py View File

@ -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)


+ 0
- 2
hc/api/tests/test_create_check.py View File

@ -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),


+ 4
- 4
hc/api/tests/test_list_checks.py View File

@ -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="[email protected]")
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"] }


+ 1
- 7
hc/front/tests/test_channel_checks.py View File

@ -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="[email protected]")
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="[email protected]", password="password")
r = self.client.get(url)
assert r.status_code == 403


+ 0
- 6
hc/front/tests/test_log.py View File

@ -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="[email protected]")
charlie.set_password("password")
charlie.save()
url = "/checks/%s/log/" % self.check.code
self.client.login(username="[email protected]", password="password")
r = self.client.get(url)


+ 1
- 7
hc/front/tests/test_remove_channel.py View File

@ -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="[email protected]")
mallory.set_password("password")
mallory.save()
self.client.login(username="[email protected]", password="password")
self.client.login(username="[email protected]", password="password")
r = self.client.post(url)
assert r.status_code == 403


+ 1
- 7
hc/front/tests/test_remove_check.py View File

@ -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="[email protected]")
mallory.set_password("password")
mallory.save()
self.client.login(username="[email protected]", password="password")
self.client.login(username="[email protected]", password="password")
r = self.client.post(url)
assert r.status_code == 403


+ 8
- 18
hc/front/tests/test_update_channel.py View File

@ -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="[email protected]")
mallory.set_password("password")
mallory.save()
payload = {"channel": self.channel.code}
self.client.login(username="mallory@example.org", password="password")
self.client.login(username="[email protected]", 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="[email protected]")
mallory.set_password("password")
mallory.save()
mc = Channel(user=mallory, kind="email")
mc.email = "[email protected]"
mc.save()
charlies_channel = Channel(user=self.charlie, kind="email")
charlies_channel.email = "[email protected]"
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):


+ 0
- 7
hc/front/tests/test_update_name.py View File

@ -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="[email protected]")
charlie.set_password("password")
charlie.save()
url = "/checks/%s/name/" % self.check.code
payload = {"name": "Charlie Sent This"}


+ 0
- 6
hc/front/tests/test_update_timeout.py View File

@ -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="[email protected]")
charlie.set_password("password")
charlie.save()
url = "/checks/%s/timeout/" % self.check.code
payload = {"timeout": 3600, "grace": 60}


+ 0
- 1
hc/front/urls.py View File

@ -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"),


+ 16
- 41
hc/front/views.py View File

@ -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()


+ 1
- 1
hc/payments/views.py View File

@ -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


+ 1
- 0
hc/settings.py View File

@ -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 = (


+ 15
- 0
hc/test.py View File

@ -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="[email protected]")
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="[email protected]")
self.charlie.set_password("password")
self.charlie.save()
charlies_profile = Profile(user=self.charlie)
charlies_profile.save()

+ 42
- 0
templates/accounts/profile.html View File

@ -131,6 +131,14 @@
</p>
{% endif %}
<br />
<a
href="#"
class="btn btn-default"
data-toggle="modal"
data-target="#set-team-name-modal">Set Team Name</a>
<a
href="#"
class="btn btn-primary pull-right"
@ -247,6 +255,40 @@
</form>
</div>
</div>
<div id="set-team-name-modal" class="modal">
<div class="modal-dialog">
<form method="post" class="form-horizontal">
{% csrf_token %}
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</span></button>
<h4 class="remove-check-title">Set Team Name</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="team-name" class="col-sm-4 control-label">Team Name</label>
<div class="col-sm-7">
<input
type="text"
class="form-control"
id="team-name"
name="team_name"
value="{{ profile }}">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button
type="submit"
name="set_team_name"
class="btn btn-primary">Set Team Name</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block scripts %}


+ 17
- 2
templates/base.html View File

@ -110,11 +110,26 @@
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a id="nav-email" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button">
{{ request.user.email }} <span class="caret"></span>
{{ request.user.profile.current_team }} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="{% url 'hc-profile' %}">Settings</a></li>
<li class="dropdown-header">{{ request.user.profile }}</li>
<li>
<a href="{% url 'hc-switch-team' request.user.username %}" class="active">Checks</a>
</li>
<li><a href="{% url 'hc-profile' %}">Account Settings</a></li>
<li role="separator" class="divider"></li>
{% for m in request.user.member_set.all %}
<li class="dropdown-header">{{ m.team }}</li>
<li>
<a href="{% url 'hc-switch-team' m.team.user.username %}">Checks</a>
</li>
<li role="separator" class="divider"></li>
{% endfor %}
<li><a href="{% url 'hc-logout' %}">Log Out</a></li>
</ul>
</li>


Loading…
Cancel
Save