forked from GithubBackups/healthchecks
Team Access, test cleanup
This commit is contained in:
parent
1bc0f82d25
commit
fdf9c607e5
@ -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())
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
hc/accounts/middleware.py
Normal file
10
hc/accounts/middleware.py
Normal 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
hc/accounts/migrations/0006_profile_current_team.py
Normal file
21
hc/accounts/migrations/0006_profile_current_team.py
Normal 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'),
|
||||
),
|
||||
]
|
@ -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):
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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")
|
||||
|
37
hc/accounts/tests/test_switch_team.py
Normal file
37
hc/accounts/tests/test_switch_team.py
Normal 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="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)
|
@ -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"),
|
||||
|
||||
|
||||
]
|
||||
|
@ -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")
|
||||
|
@ -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"))
|
||||
|
@ -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"))
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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,6 +39,7 @@ 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"})
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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"}
|
||||
|
||||
|
@ -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}
|
||||
|
||||
|
@ -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"),
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
hc/test.py
15
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()
|
||||
|
@ -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">×</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 %}
|
||||
|
@ -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…
x
Reference in New Issue
Block a user