0
0
Fork 0
mirror of https://github.com/healthchecks/healthchecks.git synced 2025-03-16 21:23:36 +00:00
healthchecks_healthchecks/hc/accounts/views.py

423 lines
13 KiB
Python
Raw Normal View History

from datetime import timedelta as td
2015-06-11 19:12:09 +00:00
import uuid
2016-07-03 15:59:07 +00:00
import re
2015-06-11 19:12:09 +00:00
from django.conf import settings
from django.contrib import messages
2015-11-02 21:55:33 +00:00
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
2015-06-18 15:39:03 +00:00
from django.contrib.auth import authenticate
from django.contrib.auth.decorators import login_required
2015-06-11 19:12:09 +00:00
from django.contrib.auth.models import User
from django.core import signing
2016-05-09 12:35:13 +00:00
from django.http import HttpResponseForbidden, HttpResponseBadRequest
2015-06-11 19:12:09 +00:00
from django.shortcuts import redirect, render
from django.utils.timezone import now
from django.views.decorators.http import require_POST
from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm,
InviteTeamMemberForm, RemoveTeamMemberForm,
ReportSettingsForm, SetPasswordForm,
TeamNameForm)
2016-05-09 08:54:18 +00:00
from hc.accounts.models import Profile, Member
2015-08-12 20:51:45 +00:00
from hc.api.models import Channel, Check
2016-07-03 15:59:07 +00:00
from hc.lib.badges import get_badge_url
from hc.payments.models import Subscription
2015-06-11 19:12:09 +00:00
2015-06-18 15:39:03 +00:00
def _make_user(email):
username = str(uuid.uuid4())[:30]
user = User(username=username, email=email)
user.set_unusable_password()
2015-06-18 15:39:03 +00:00
user.save()
2015-06-12 17:49:35 +00:00
# Ensure a profile gets created
Profile.objects.for_user(user)
2016-05-09 12:35:13 +00:00
2015-08-12 20:51:45 +00:00
channel = Channel()
channel.user = user
channel.kind = "email"
channel.value = email
channel.email_verified = True
channel.save()
2015-06-18 15:39:03 +00:00
return user
2015-06-12 17:49:35 +00:00
2015-06-30 21:28:13 +00:00
def _associate_demo_check(request, user):
if "welcome_code" not in request.session:
return
try:
2015-06-30 21:28:13 +00:00
check = Check.objects.get(code=request.session["welcome_code"])
except Check.DoesNotExist:
return
# Only associate demo check if it doesn't have an owner already.
if check.user:
return
check.user = user
check.save()
2015-08-12 20:51:45 +00:00
check.assign_all_channels()
2015-08-12 20:51:45 +00:00
del request.session["welcome_code"]
2015-06-30 21:28:13 +00:00
2017-08-30 19:42:45 +00:00
def _ensure_own_team(request):
""" Make sure user is switched to their own team. """
if request.team != request.profile:
request.team = request.profile
request.profile.current_team = request.profile
request.profile.save()
def login(request, show_password=False):
bad_credentials = False
2015-06-11 19:12:09 +00:00
if request.method == 'POST':
form = EmailPasswordForm(request.POST)
2015-06-11 19:12:09 +00:00
if form.is_valid():
2015-06-18 15:39:03 +00:00
email = form.cleaned_data["email"]
password = form.cleaned_data["password"]
if len(password):
user = authenticate(username=email, password=password)
if user is not None and user.is_active:
auth_login(request, user)
return redirect("hc-checks")
bad_credentials = True
show_password = True
else:
user = None
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
if settings.REGISTRATION_OPEN:
user = _make_user(email)
_associate_demo_check(request, user)
else:
bad_credentials = True
if user:
profile = Profile.objects.for_user(user)
profile.send_instant_login_link()
return redirect("hc-login-link-sent")
2015-06-11 19:12:09 +00:00
else:
form = EmailPasswordForm()
2015-06-11 19:12:09 +00:00
bad_link = request.session.pop("bad_link", None)
ctx = {
"form": form,
"bad_credentials": bad_credentials,
"bad_link": bad_link,
"show_password": show_password
}
2015-06-11 19:12:09 +00:00
return render(request, "accounts/login.html", ctx)
2015-06-18 15:39:03 +00:00
def logout(request):
auth_logout(request)
return redirect("hc-index")
2015-06-11 19:12:09 +00:00
def login_link_sent(request):
return render(request, "accounts/login_link_sent.html")
def link_sent(request):
return render(request, "accounts/link_sent.html")
def check_token(request, username, token):
2016-08-29 14:54:53 +00:00
if request.user.is_authenticated and request.user.username == username:
2015-11-28 08:35:02 +00:00
# User is already logged in
return redirect("hc-checks")
# Some email servers open links in emails to check for malicious content.
# To work around this, we sign user in if the method is POST.
#
# If the method is GET, we instead serve a HTML form and a piece
# of Javascript to automatically submit it.
2015-08-01 20:02:51 +00:00
if request.method == "POST":
user = authenticate(username=username, token=token)
if user is not None and user.is_active:
# This should get rid of "welcome_code" in session
request.session.flush()
2015-08-01 20:02:51 +00:00
user.profile.token = ""
user.profile.save()
auth_login(request, user)
return redirect("hc-checks")
request.session["bad_link"] = True
return redirect("hc-login")
return render(request, "accounts/check_token_submit.html")
@login_required
def profile(request):
2017-08-30 19:42:45 +00:00
_ensure_own_team(request)
profile = request.profile
ctx = {
"page": "profile",
"profile": profile,
"show_api_key": False,
"api_status": "default",
"team_status": "default"
}
if request.method == "POST":
if "change_email" in request.POST:
profile.send_change_email_link()
return redirect("hc-link-sent")
elif "set_password" in request.POST:
profile.send_set_password_link()
return redirect("hc-link-sent")
elif "create_api_key" in request.POST:
profile.set_api_key()
ctx["show_api_key"] = True
ctx["api_key_created"] = True
ctx["api_status"] = "success"
elif "revoke_api_key" in request.POST:
profile.api_key = ""
profile.save()
ctx["api_key_revoked"] = True
ctx["api_status"] = "info"
elif "show_api_key" in request.POST:
ctx["show_api_key"] = True
2016-05-09 08:54:18 +00:00
elif "invite_team_member" in request.POST:
if not profile.can_invite():
return HttpResponseForbidden()
2016-05-09 08:54:18 +00:00
form = InviteTeamMemberForm(request.POST)
if form.is_valid():
email = form.cleaned_data["email"]
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
user = _make_user(email)
profile.invite(user)
ctx["team_member_invited"] = email
ctx["team_status"] = "success"
2016-05-09 08:54:18 +00:00
elif "remove_team_member" in request.POST:
form = RemoveTeamMemberForm(request.POST)
if form.is_valid():
email = form.cleaned_data["email"]
2016-05-09 14:29:41 +00:00
farewell_user = User.objects.get(email=email)
farewell_user.profile.current_team = None
farewell_user.profile.save()
Member.objects.filter(team=profile,
user=farewell_user).delete()
ctx["team_member_removed"] = email
ctx["team_status"] = "info"
2016-05-09 12:35:13 +00:00
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()
ctx["team_name_updated"] = True
ctx["team_status"] = "success"
return render(request, "accounts/profile.html", ctx)
@login_required
def notifications(request):
2017-08-30 19:42:45 +00:00
_ensure_own_team(request)
profile = request.profile
ctx = {
"status": "default",
"page": "profile",
"profile": profile
}
if request.method == "POST":
form = ReportSettingsForm(request.POST)
if form.is_valid():
if profile.reports_allowed != form.cleaned_data["reports_allowed"]:
profile.reports_allowed = form.cleaned_data["reports_allowed"]
if profile.reports_allowed:
profile.next_report_date = now() + td(days=30)
else:
profile.next_report_date = None
if profile.nag_period != form.cleaned_data["nag_period"]:
# Set the new nag period
profile.nag_period = form.cleaned_data["nag_period"]
# and schedule next_nag_date:
if profile.nag_period:
profile.next_nag_date = now() + profile.nag_period
else:
profile.next_nag_date = None
profile.save()
ctx["status"] = "info"
return render(request, "accounts/notifications.html", ctx)
@login_required
def badges(request):
2017-08-30 19:42:45 +00:00
_ensure_own_team(request)
2016-07-03 15:59:07 +00:00
tags = set()
for check in Check.objects.filter(user=request.team.user):
tags.update(check.tags_list())
2017-08-30 19:42:45 +00:00
username = request.user.username
urls = []
2016-07-03 15:59:07 +00:00
for tag in sorted(tags, key=lambda s: s.lower()):
if not re.match("^[\w-]+$", tag):
continue
urls.append({
"svg": get_badge_url(username, tag),
"json": get_badge_url(username, tag, format="json"),
})
2016-07-03 15:59:07 +00:00
ctx = {
2016-08-29 13:45:47 +00:00
"page": "profile",
2017-11-10 19:52:27 +00:00
"urls": urls,
"master": {
"svg": get_badge_url(username, "*"),
"json": get_badge_url(username, "*", format="json")
}
}
return render(request, "accounts/badges.html", ctx)
@login_required
def set_password(request, token):
2017-08-30 19:42:45 +00:00
if not request.profile.check_token(token, "set-password"):
return HttpResponseBadRequest()
if request.method == "POST":
form = SetPasswordForm(request.POST)
if form.is_valid():
password = form.cleaned_data["password"]
request.user.set_password(password)
request.user.save()
2017-08-30 19:42:45 +00:00
request.profile.token = ""
request.profile.save()
# Setting a password logs the user out, so here we
# log them back in.
u = authenticate(username=request.user.email, password=password)
auth_login(request, u)
2016-07-07 21:05:05 +00:00
messages.success(request, "Your password has been set!")
return redirect("hc-profile")
2016-02-16 21:41:40 +00:00
return render(request, "accounts/set_password.html", {})
@login_required
def change_email(request, token):
2017-08-30 19:42:45 +00:00
if not request.profile.check_token(token, "change-email"):
return HttpResponseBadRequest()
if request.method == "POST":
form = ChangeEmailForm(request.POST)
if form.is_valid():
request.user.email = form.cleaned_data["email"]
request.user.set_unusable_password()
request.user.save()
2017-08-30 19:42:45 +00:00
request.profile.token = ""
request.profile.save()
return redirect("hc-change-email-done")
else:
form = ChangeEmailForm()
return render(request, "accounts/change_email.html", {"form": form})
def change_email_done(request):
return render(request, "accounts/change_email_done.html")
def unsubscribe_reports(request, username):
if ":" in username:
signer = signing.TimestampSigner(salt="reports")
try:
username = signer.unsign(username)
except signing.BadSignature:
return render(request, "bad_link.html")
else:
# Username is not signed but there should be a ?token=... parameter
# This is here for backwards compatibility and will be removed
# at some point.
try:
signing.Signer().unsign(request.GET.get("token"))
except signing.BadSignature:
return render(request, "bad_link.html")
user = User.objects.get(username=username)
2017-08-30 19:42:45 +00:00
profile = Profile.objects.for_user(user)
profile.reports_allowed = False
profile.nag_period = td()
2017-08-30 19:42:45 +00:00
profile.save()
return render(request, "accounts/unsubscribed.html")
2016-05-09 12:35:13 +00:00
@login_required
2016-05-09 12:35:13 +00:00
def switch_team(request, target_username):
try:
target_team = Profile.objects.get(user__username=target_username)
except Profile.DoesNotExist:
return HttpResponseForbidden()
2016-05-09 12:35:13 +00:00
# The rules:
2016-05-09 12:35:13 +00:00
# Superuser can switch to any team.
access_ok = request.user.is_superuser
# Users can switch to their own teams.
if not access_ok and target_team == request.profile:
access_ok = True
# Users can switch to teams they are members of.
if not access_ok:
access_ok = request.user.memberships.filter(team=target_team).exists()
if not access_ok:
return HttpResponseForbidden()
2016-05-09 12:35:13 +00:00
request.profile.current_team = target_team
2017-08-30 19:42:45 +00:00
request.profile.save()
2016-05-09 12:35:13 +00:00
return redirect("hc-checks")
@require_POST
@login_required
def close(request):
user = request.user
# Subscription needs to be canceled before it is deleted:
sub = Subscription.objects.filter(user=user).first()
if sub:
sub.cancel()
user.delete()
# Deleting user also deletes its profile, checks, channels etc.
request.session.flush()
return redirect("hc-index")