Team Access, test cleanup

This commit is contained in:
Pēteris Caune 2016-05-09 15:35:13 +03:00
parent 1bc0f82d25
commit fdf9c607e5
30 changed files with 255 additions and 162 deletions

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

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)

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
hc/accounts/middleware.py Normal file
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

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

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

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

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

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

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

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

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

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

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)

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

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="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"] }

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="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

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

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="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

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="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

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="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):

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

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

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

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

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

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

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="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()

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 %}

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>