2017-10-14 13:03:56 +00:00
|
|
|
from datetime import timedelta as td
|
2020-02-27 13:52:00 +00:00
|
|
|
from urllib.parse import urlparse
|
2015-06-11 19:12:09 +00:00
|
|
|
import uuid
|
|
|
|
|
2017-02-24 13:58:11 +00:00
|
|
|
from django.conf import settings
|
2015-12-15 00:27:24 +00:00
|
|
|
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
|
2015-12-15 00:27:24 +00:00
|
|
|
from django.contrib.auth.decorators import login_required
|
2015-06-11 19:12:09 +00:00
|
|
|
from django.contrib.auth.models import User
|
2015-12-15 00:27:24 +00:00
|
|
|
from django.core import signing
|
2019-05-15 11:27:50 +00:00
|
|
|
from django.http import (
|
|
|
|
HttpResponseForbidden,
|
|
|
|
HttpResponseBadRequest,
|
|
|
|
HttpResponseNotFound,
|
|
|
|
)
|
2019-01-29 17:57:18 +00:00
|
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
2017-10-14 13:03:56 +00:00
|
|
|
from django.utils.timezone import now
|
2018-11-28 20:06:12 +00:00
|
|
|
from django.urls import resolve, Resolver404
|
2018-10-24 07:06:51 +00:00
|
|
|
from django.views.decorators.csrf import csrf_exempt
|
2017-03-16 17:39:30 +00:00
|
|
|
from django.views.decorators.http import require_POST
|
2020-04-13 09:16:39 +00:00
|
|
|
from hc.accounts import forms
|
2019-01-12 14:40:21 +00:00
|
|
|
from hc.accounts.models import Profile, Project, Member
|
2019-04-25 18:28:40 +00:00
|
|
|
from hc.api.models import Channel, Check, TokenBucket
|
2019-10-12 17:07:09 +00:00
|
|
|
from hc.lib.date import choose_next_report_date
|
2017-03-16 17:39:30 +00:00
|
|
|
from hc.payments.models import Subscription
|
2015-06-11 19:12:09 +00:00
|
|
|
|
2019-05-15 11:27:50 +00:00
|
|
|
NEXT_WHITELIST = (
|
|
|
|
"hc-checks",
|
|
|
|
"hc-details",
|
|
|
|
"hc-log",
|
2020-02-25 12:22:34 +00:00
|
|
|
"hc-p-channels",
|
2019-05-15 11:27:50 +00:00
|
|
|
"hc-add-slack",
|
|
|
|
"hc-add-pushover",
|
2020-02-27 13:52:00 +00:00
|
|
|
"hc-add-telegram",
|
2020-04-13 12:04:59 +00:00
|
|
|
"hc-project-settings",
|
2019-05-15 11:27:50 +00:00
|
|
|
)
|
2018-11-28 20:06:12 +00:00
|
|
|
|
|
|
|
|
2020-02-27 13:52:00 +00:00
|
|
|
def _is_whitelisted(redirect_url):
|
|
|
|
if not redirect_url:
|
|
|
|
return False
|
|
|
|
|
|
|
|
parsed = urlparse(redirect_url)
|
2018-11-28 20:06:12 +00:00
|
|
|
try:
|
2020-02-27 13:52:00 +00:00
|
|
|
match = resolve(parsed.path)
|
2018-11-28 20:06:12 +00:00
|
|
|
except Resolver404:
|
|
|
|
return False
|
|
|
|
|
|
|
|
return match.url_name in NEXT_WHITELIST
|
2018-11-26 15:32:23 +00:00
|
|
|
|
2015-06-11 19:12:09 +00:00
|
|
|
|
2019-01-29 17:16:52 +00:00
|
|
|
def _make_user(email, with_project=True):
|
2019-10-12 08:37:06 +00:00
|
|
|
username = str(uuid.uuid4())[:30]
|
2015-06-18 15:39:03 +00:00
|
|
|
user = User(username=username, email=email)
|
2016-01-04 20:39:49 +00:00
|
|
|
user.set_unusable_password()
|
2015-06-18 15:39:03 +00:00
|
|
|
user.save()
|
2015-06-12 17:49:35 +00:00
|
|
|
|
2019-01-29 17:16:52 +00:00
|
|
|
project = None
|
|
|
|
if with_project:
|
|
|
|
project = Project(owner=user)
|
|
|
|
project.badge_key = user.username
|
|
|
|
project.save()
|
|
|
|
|
|
|
|
check = Check(project=project)
|
|
|
|
check.name = "My First Check"
|
|
|
|
check.save()
|
|
|
|
|
|
|
|
channel = Channel(project=project)
|
|
|
|
channel.kind = "email"
|
|
|
|
channel.value = email
|
|
|
|
channel.email_verified = True
|
|
|
|
channel.save()
|
|
|
|
|
|
|
|
channel.checks.add(check)
|
2019-01-12 14:40:21 +00:00
|
|
|
|
2017-02-24 13:58:11 +00:00
|
|
|
# Ensure a profile gets created
|
2020-02-27 10:34:21 +00:00
|
|
|
Profile.objects.for_user(user)
|
2016-05-09 12:35:13 +00:00
|
|
|
|
2018-06-14 20:42:39 +00:00
|
|
|
return user
|
2015-06-30 21:28:13 +00:00
|
|
|
|
|
|
|
|
2018-11-26 15:32:23 +00:00
|
|
|
def _redirect_after_login(request):
|
|
|
|
""" Redirect to the URL indicated in ?next= query parameter. """
|
|
|
|
|
|
|
|
redirect_url = request.GET.get("next")
|
2018-11-28 20:06:12 +00:00
|
|
|
if _is_whitelisted(redirect_url):
|
2018-11-26 15:32:23 +00:00
|
|
|
return redirect(redirect_url)
|
|
|
|
|
2019-01-29 17:05:32 +00:00
|
|
|
if request.user.project_set.count() == 1:
|
|
|
|
project = request.user.project_set.first()
|
|
|
|
return redirect("hc-checks", project.code)
|
|
|
|
|
2019-01-29 08:59:10 +00:00
|
|
|
return redirect("hc-index")
|
2018-11-26 15:32:23 +00:00
|
|
|
|
|
|
|
|
2018-10-09 13:12:02 +00:00
|
|
|
def login(request):
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.PasswordLoginForm()
|
|
|
|
magic_form = forms.EmailLoginForm()
|
2018-10-09 13:12:02 +00:00
|
|
|
|
2019-05-15 11:27:50 +00:00
|
|
|
if request.method == "POST":
|
2018-10-09 13:12:02 +00:00
|
|
|
if request.POST.get("action") == "login":
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.PasswordLoginForm(request.POST)
|
2018-10-09 13:12:02 +00:00
|
|
|
if form.is_valid():
|
|
|
|
auth_login(request, form.user)
|
2018-11-26 15:32:23 +00:00
|
|
|
return _redirect_after_login(request)
|
2018-10-09 13:12:02 +00:00
|
|
|
|
|
|
|
else:
|
2020-04-13 09:16:39 +00:00
|
|
|
magic_form = forms.EmailLoginForm(request.POST)
|
2018-10-09 13:12:02 +00:00
|
|
|
if magic_form.is_valid():
|
2018-11-26 15:32:23 +00:00
|
|
|
redirect_url = request.GET.get("next")
|
2019-04-25 18:28:40 +00:00
|
|
|
if not _is_whitelisted(redirect_url):
|
|
|
|
redirect_url = None
|
2018-11-26 15:32:23 +00:00
|
|
|
|
2019-04-26 12:51:10 +00:00
|
|
|
profile = Profile.objects.for_user(magic_form.user)
|
2019-04-25 18:28:40 +00:00
|
|
|
profile.send_instant_login_link(redirect_url=redirect_url)
|
2019-05-21 08:26:55 +00:00
|
|
|
response = redirect("hc-login-link-sent")
|
|
|
|
|
|
|
|
# check_token_submit looks for this cookie to decide if
|
|
|
|
# it needs to do the extra POST step.
|
|
|
|
response.set_cookie("auto-login", "1", max_age=300, httponly=True)
|
|
|
|
return response
|
2015-06-11 19:12:09 +00:00
|
|
|
|
2015-12-19 08:49:55 +00:00
|
|
|
bad_link = request.session.pop("bad_link", None)
|
2016-01-04 22:25:08 +00:00
|
|
|
ctx = {
|
2018-10-09 13:12:02 +00:00
|
|
|
"page": "login",
|
2016-01-04 22:25:08 +00:00
|
|
|
"form": form,
|
2018-10-09 13:12:02 +00:00
|
|
|
"magic_form": magic_form,
|
2019-05-15 11:27:50 +00:00
|
|
|
"bad_link": bad_link,
|
2019-08-26 07:55:41 +00:00
|
|
|
"registration_open": settings.REGISTRATION_OPEN,
|
2016-01-04 22:25:08 +00:00
|
|
|
}
|
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")
|
|
|
|
|
|
|
|
|
2018-10-12 07:55:15 +00:00
|
|
|
@require_POST
|
2019-12-06 06:58:32 +00:00
|
|
|
@csrf_exempt
|
2018-10-12 07:55:15 +00:00
|
|
|
def signup(request):
|
|
|
|
if not settings.REGISTRATION_OPEN:
|
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
|
|
|
ctx = {}
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.AvailableEmailForm(request.POST)
|
2018-10-12 07:55:15 +00:00
|
|
|
if form.is_valid():
|
|
|
|
email = form.cleaned_data["identity"]
|
|
|
|
user = _make_user(email)
|
|
|
|
profile = Profile.objects.for_user(user)
|
|
|
|
profile.send_instant_login_link()
|
|
|
|
ctx["created"] = True
|
|
|
|
else:
|
|
|
|
ctx = {"form": form}
|
|
|
|
|
2019-10-12 17:14:57 +00:00
|
|
|
response = render(request, "accounts/signup_result.html", ctx)
|
|
|
|
if ctx.get("created"):
|
|
|
|
response.set_cookie("auto-login", "1", max_age=300, httponly=True)
|
|
|
|
|
|
|
|
return response
|
2018-10-12 07:55:15 +00:00
|
|
|
|
|
|
|
|
2015-06-11 19:12:09 +00:00
|
|
|
def login_link_sent(request):
|
|
|
|
return render(request, "accounts/login_link_sent.html")
|
|
|
|
|
|
|
|
|
2017-08-23 12:47:20 +00:00
|
|
|
def link_sent(request):
|
|
|
|
return render(request, "accounts/link_sent.html")
|
2016-01-04 22:25:08 +00:00
|
|
|
|
|
|
|
|
2015-06-11 20:44:49 +00:00
|
|
|
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
|
2018-11-26 15:32:23 +00:00
|
|
|
return _redirect_after_login(request)
|
2015-11-28 08:35:02 +00:00
|
|
|
|
2016-07-28 18:41:28 +00:00
|
|
|
# Some email servers open links in emails to check for malicious content.
|
2019-05-21 08:26:55 +00:00
|
|
|
# To work around this, we sign user in if the method is POST
|
|
|
|
# *or* if the browser presents a cookie we had set when sending the login link.
|
2016-07-28 18:41:28 +00:00
|
|
|
#
|
|
|
|
# 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
|
|
|
|
2019-05-21 08:26:55 +00:00
|
|
|
if request.method == "POST" or "auto-login" in request.COOKIES:
|
2016-07-28 18:41:28 +00:00
|
|
|
user = authenticate(username=username, token=token)
|
|
|
|
if user is not None and user.is_active:
|
|
|
|
user.profile.token = ""
|
|
|
|
user.profile.save()
|
|
|
|
auth_login(request, user)
|
|
|
|
|
2018-11-26 15:32:23 +00:00
|
|
|
return _redirect_after_login(request)
|
2016-07-28 18:41:28 +00:00
|
|
|
|
|
|
|
request.session["bad_link"] = True
|
|
|
|
return redirect("hc-login")
|
2015-06-11 20:44:49 +00:00
|
|
|
|
2016-07-28 18:41:28 +00:00
|
|
|
return render(request, "accounts/check_token_submit.html")
|
2015-12-15 00:27:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
|
def profile(request):
|
2017-08-30 19:42:45 +00:00
|
|
|
profile = request.profile
|
2015-12-15 00:27:24 +00:00
|
|
|
|
2019-05-15 11:27:50 +00:00
|
|
|
ctx = {"page": "profile", "profile": profile, "my_projects_status": "default"}
|
2017-08-23 12:47:20 +00:00
|
|
|
|
2015-12-15 00:27:24 +00:00
|
|
|
if request.method == "POST":
|
2017-08-23 12:47:20 +00:00
|
|
|
if "change_email" in request.POST:
|
|
|
|
profile.send_change_email_link()
|
|
|
|
return redirect("hc-link-sent")
|
|
|
|
elif "set_password" in request.POST:
|
2016-01-04 22:25:08 +00:00
|
|
|
profile.send_set_password_link()
|
2017-08-23 12:47:20 +00:00
|
|
|
return redirect("hc-link-sent")
|
2019-01-28 18:09:23 +00:00
|
|
|
elif "leave_project" in request.POST:
|
|
|
|
code = request.POST["code"]
|
|
|
|
try:
|
2019-05-15 11:27:50 +00:00
|
|
|
project = Project.objects.get(code=code, member__user=request.user)
|
2019-01-28 18:09:23 +00:00
|
|
|
except Project.DoesNotExist:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
Member.objects.filter(project=project, user=request.user).delete()
|
|
|
|
|
|
|
|
ctx["left_project"] = project
|
|
|
|
ctx["my_projects_status"] = "info"
|
|
|
|
|
|
|
|
# Retrieve projects right before rendering the template--
|
|
|
|
# The list of the projects might have *just* changed
|
|
|
|
ctx["projects"] = list(profile.projects())
|
2019-01-22 13:44:54 +00:00
|
|
|
return render(request, "accounts/profile.html", ctx)
|
|
|
|
|
|
|
|
|
2019-01-28 18:09:23 +00:00
|
|
|
@login_required
|
|
|
|
@require_POST
|
|
|
|
def add_project(request):
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.ProjectNameForm(request.POST)
|
2019-01-28 18:09:23 +00:00
|
|
|
if not form.is_valid():
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
project = Project(owner=request.user)
|
|
|
|
project.code = project.badge_key = str(uuid.uuid4())
|
|
|
|
project.name = form.cleaned_data["name"]
|
|
|
|
project.save()
|
|
|
|
|
2019-01-29 08:59:10 +00:00
|
|
|
return redirect("hc-checks", project.code)
|
2019-01-28 18:09:23 +00:00
|
|
|
|
|
|
|
|
2019-01-22 13:44:54 +00:00
|
|
|
@login_required
|
|
|
|
def project(request, code):
|
2019-04-02 08:51:35 +00:00
|
|
|
if request.user.is_superuser:
|
|
|
|
q = Project.objects
|
|
|
|
else:
|
|
|
|
q = request.profile.projects()
|
|
|
|
|
|
|
|
try:
|
|
|
|
project = q.get(code=code)
|
|
|
|
except Project.DoesNotExist:
|
|
|
|
return HttpResponseNotFound()
|
2019-01-22 13:44:54 +00:00
|
|
|
|
2019-04-02 08:51:35 +00:00
|
|
|
is_owner = project.owner_id == request.user.id
|
2019-01-22 13:44:54 +00:00
|
|
|
ctx = {
|
2019-01-28 18:09:23 +00:00
|
|
|
"page": "project",
|
2019-01-22 13:44:54 +00:00
|
|
|
"project": project,
|
2019-04-02 08:51:35 +00:00
|
|
|
"is_owner": is_owner,
|
|
|
|
"show_api_keys": "show_api_keys" in request.GET,
|
2019-01-22 13:44:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if request.method == "POST":
|
|
|
|
if "create_api_keys" in request.POST:
|
2019-01-17 14:26:45 +00:00
|
|
|
project.set_api_keys()
|
2019-01-17 14:02:57 +00:00
|
|
|
project.save()
|
2019-01-12 14:40:21 +00:00
|
|
|
|
2018-10-29 19:44:34 +00:00
|
|
|
ctx["show_api_keys"] = True
|
|
|
|
ctx["api_keys_created"] = True
|
2017-08-23 12:47:20 +00:00
|
|
|
ctx["api_status"] = "success"
|
2018-10-29 19:44:34 +00:00
|
|
|
elif "revoke_api_keys" in request.POST:
|
2019-01-17 14:02:57 +00:00
|
|
|
project.api_key = ""
|
|
|
|
project.api_key_readonly = ""
|
|
|
|
project.save()
|
2019-01-12 14:40:21 +00:00
|
|
|
|
2018-10-29 19:44:34 +00:00
|
|
|
ctx["api_keys_revoked"] = True
|
2017-08-23 12:47:20 +00:00
|
|
|
ctx["api_status"] = "info"
|
2018-10-29 19:44:34 +00:00
|
|
|
elif "show_api_keys" in request.POST:
|
|
|
|
ctx["show_api_keys"] = True
|
2016-05-09 08:54:18 +00:00
|
|
|
elif "invite_team_member" in request.POST:
|
2020-02-14 11:05:21 +00:00
|
|
|
if not is_owner:
|
2016-05-14 09:51:10 +00:00
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.InviteTeamMemberForm(request.POST)
|
2016-05-09 08:54:18 +00:00
|
|
|
if form.is_valid():
|
|
|
|
email = form.cleaned_data["email"]
|
2020-02-14 11:05:21 +00:00
|
|
|
|
2020-04-12 11:46:12 +00:00
|
|
|
invite_suggestions = project.invite_suggestions()
|
2020-02-14 11:05:21 +00:00
|
|
|
if not invite_suggestions.filter(email=email).exists():
|
|
|
|
# We're inviting a new user. Are we within team size limit?
|
|
|
|
if not project.can_invite_new_users():
|
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
|
|
|
# And are we not hitting a rate limit?
|
|
|
|
if not TokenBucket.authorize_invite(request.user):
|
|
|
|
return render(request, "try_later.html")
|
|
|
|
|
2016-05-09 08:54:18 +00:00
|
|
|
try:
|
|
|
|
user = User.objects.get(email=email)
|
|
|
|
except User.DoesNotExist:
|
2019-01-29 17:16:52 +00:00
|
|
|
user = _make_user(email, with_project=False)
|
2016-05-09 08:54:18 +00:00
|
|
|
|
2019-01-22 13:44:54 +00:00
|
|
|
project.invite(user)
|
2017-08-23 12:47:20 +00:00
|
|
|
ctx["team_member_invited"] = email
|
|
|
|
ctx["team_status"] = "success"
|
|
|
|
|
2016-05-09 08:54:18 +00:00
|
|
|
elif "remove_team_member" in request.POST:
|
2019-04-02 08:51:35 +00:00
|
|
|
if not is_owner:
|
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.RemoveTeamMemberForm(request.POST)
|
2016-05-09 08:54:18 +00:00
|
|
|
if form.is_valid():
|
2019-01-29 14:42:12 +00:00
|
|
|
q = User.objects
|
|
|
|
q = q.filter(email=form.cleaned_data["email"])
|
|
|
|
q = q.filter(memberships__project=project)
|
|
|
|
farewell_user = q.first()
|
|
|
|
if farewell_user is None:
|
|
|
|
return HttpResponseBadRequest()
|
2016-05-09 08:54:18 +00:00
|
|
|
|
2019-05-15 11:27:50 +00:00
|
|
|
Member.objects.filter(project=project, user=farewell_user).delete()
|
2016-05-09 14:29:41 +00:00
|
|
|
|
2019-01-29 14:42:12 +00:00
|
|
|
ctx["team_member_removed"] = form.cleaned_data["email"]
|
2017-08-23 12:47:20 +00:00
|
|
|
ctx["team_status"] = "info"
|
2019-01-22 13:44:54 +00:00
|
|
|
elif "set_project_name" in request.POST:
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.ProjectNameForm(request.POST)
|
2016-05-09 12:35:13 +00:00
|
|
|
if form.is_valid():
|
2019-01-22 13:44:54 +00:00
|
|
|
project.name = form.cleaned_data["name"]
|
|
|
|
project.save()
|
2019-01-12 14:40:21 +00:00
|
|
|
|
2019-01-22 13:44:54 +00:00
|
|
|
ctx["project_name_updated"] = True
|
|
|
|
ctx["project_name_status"] = "success"
|
|
|
|
|
2020-04-12 11:46:12 +00:00
|
|
|
elif "transfer_project" in request.POST:
|
|
|
|
if not is_owner:
|
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.TransferForm(request.POST)
|
2020-04-12 11:46:12 +00:00
|
|
|
if form.is_valid():
|
|
|
|
email = form.cleaned_data["email"]
|
|
|
|
|
|
|
|
# Revoke any previous transfer requests
|
|
|
|
project.member_set.update(transfer_request_date=None)
|
|
|
|
|
|
|
|
# Initiate the new request
|
|
|
|
q = project.member_set.filter(user__email=email)
|
|
|
|
q.update(transfer_request_date=now())
|
|
|
|
|
|
|
|
ctx["transfer_initiated"] = True
|
|
|
|
ctx["transfer_status"] = "success"
|
|
|
|
|
2020-04-13 12:04:59 +00:00
|
|
|
profile = Profile.objects.get(user__email=email)
|
|
|
|
profile.send_transfer_request(project)
|
2020-04-12 11:46:12 +00:00
|
|
|
|
|
|
|
elif "cancel_transfer" in request.POST:
|
|
|
|
if not is_owner:
|
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
|
|
|
project.member_set.update(transfer_request_date=None)
|
|
|
|
ctx["transfer_cancelled"] = True
|
|
|
|
ctx["transfer_status"] = "success"
|
|
|
|
|
|
|
|
elif "accept_transfer" in request.POST:
|
|
|
|
tr = project.transfer_request()
|
|
|
|
if not tr or tr.user != request.user:
|
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
|
|
|
if not tr.can_accept():
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
# 1. Remove user's membership
|
|
|
|
tr.delete()
|
|
|
|
|
|
|
|
# 2. Invite the current owner as a member
|
|
|
|
Member.objects.create(user=project.owner, project=project)
|
|
|
|
|
|
|
|
# 3. Change project's owner
|
|
|
|
project.owner = request.user
|
|
|
|
project.save()
|
|
|
|
|
|
|
|
ctx["is_owner"] = True
|
|
|
|
messages.success(request, "You are now the owner of this project!")
|
|
|
|
|
|
|
|
elif "reject_transfer" in request.POST:
|
|
|
|
tr = project.transfer_request()
|
|
|
|
if not tr or tr.user != request.user:
|
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
|
|
|
tr.transfer_request_date = None
|
|
|
|
tr.save()
|
|
|
|
|
2019-01-22 13:44:54 +00:00
|
|
|
return render(request, "accounts/project.html", ctx)
|
2017-03-16 14:06:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
|
def notifications(request):
|
2017-08-30 19:42:45 +00:00
|
|
|
profile = request.profile
|
2017-03-16 14:06:22 +00:00
|
|
|
|
2019-05-15 11:27:50 +00:00
|
|
|
ctx = {"status": "default", "page": "profile", "profile": profile}
|
2017-10-14 19:04:11 +00:00
|
|
|
|
2017-03-16 14:06:22 +00:00
|
|
|
if request.method == "POST":
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.ReportSettingsForm(request.POST)
|
2017-03-16 14:06:22 +00:00
|
|
|
if form.is_valid():
|
2017-10-14 13:03:56 +00:00
|
|
|
if profile.reports_allowed != form.cleaned_data["reports_allowed"]:
|
|
|
|
profile.reports_allowed = form.cleaned_data["reports_allowed"]
|
|
|
|
if profile.reports_allowed:
|
2019-10-12 17:07:09 +00:00
|
|
|
profile.next_report_date = choose_next_report_date()
|
2017-10-14 13:03:56 +00:00
|
|
|
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
|
|
|
|
|
2017-03-16 14:06:22 +00:00
|
|
|
profile.save()
|
2017-10-14 19:04:11 +00:00
|
|
|
ctx["status"] = "info"
|
2017-03-16 14:06:22 +00:00
|
|
|
|
|
|
|
return render(request, "accounts/notifications.html", ctx)
|
|
|
|
|
|
|
|
|
2016-01-04 22:25:08 +00:00
|
|
|
@login_required
|
|
|
|
def set_password(request, token):
|
2017-08-30 19:42:45 +00:00
|
|
|
if not request.profile.check_token(token, "set-password"):
|
2016-01-04 22:25:08 +00:00
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
if request.method == "POST":
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.SetPasswordForm(request.POST)
|
2016-01-04 22:25:08 +00:00
|
|
|
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()
|
2016-01-04 22:25:08 +00:00
|
|
|
|
|
|
|
# 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!")
|
2016-01-04 22:25:08 +00:00
|
|
|
return redirect("hc-profile")
|
|
|
|
|
2016-02-16 21:41:40 +00:00
|
|
|
return render(request, "accounts/set_password.html", {})
|
2016-01-04 22:25:08 +00:00
|
|
|
|
|
|
|
|
2017-08-23 12:47:20 +00:00
|
|
|
@login_required
|
|
|
|
def change_email(request, token):
|
2017-08-30 19:42:45 +00:00
|
|
|
if not request.profile.check_token(token, "change-email"):
|
2017-08-23 12:47:20 +00:00
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
|
|
|
if request.method == "POST":
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.ChangeEmailForm(request.POST)
|
2017-08-23 12:47:20 +00:00
|
|
|
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()
|
2017-08-23 12:47:20 +00:00
|
|
|
|
|
|
|
return redirect("hc-change-email-done")
|
|
|
|
else:
|
2020-04-13 09:16:39 +00:00
|
|
|
form = forms.ChangeEmailForm()
|
2017-08-23 12:47:20 +00:00
|
|
|
|
|
|
|
return render(request, "accounts/change_email.html", {"form": form})
|
|
|
|
|
|
|
|
|
|
|
|
def change_email_done(request):
|
|
|
|
return render(request, "accounts/change_email_done.html")
|
|
|
|
|
|
|
|
|
2018-10-24 07:06:51 +00:00
|
|
|
@csrf_exempt
|
2019-12-18 14:10:30 +00:00
|
|
|
def unsubscribe_reports(request, signed_username):
|
|
|
|
# Some email servers open links in emails to check for malicious content.
|
|
|
|
# To work around this, for GET requests we serve a confirmation form.
|
|
|
|
# If the signature is more than 5 minutes old, we also include JS code to
|
|
|
|
# auto-submit the form.
|
|
|
|
|
|
|
|
ctx = {}
|
2018-05-25 20:38:02 +00:00
|
|
|
signer = signing.TimestampSigner(salt="reports")
|
2019-12-18 14:10:30 +00:00
|
|
|
# First, check the signature without looking at the timestamp:
|
2018-05-25 20:38:02 +00:00
|
|
|
try:
|
2019-12-18 14:10:30 +00:00
|
|
|
username = signer.unsign(signed_username)
|
2018-05-25 20:38:02 +00:00
|
|
|
except signing.BadSignature:
|
|
|
|
return render(request, "bad_link.html")
|
2015-12-15 00:27:24 +00:00
|
|
|
|
2019-12-18 14:10:30 +00:00
|
|
|
# Check if timestamp is older than 5 minutes:
|
|
|
|
try:
|
|
|
|
username = signer.unsign(signed_username, max_age=300)
|
|
|
|
except signing.SignatureExpired:
|
|
|
|
ctx["autosubmit"] = True
|
|
|
|
|
2019-12-10 07:14:54 +00:00
|
|
|
if request.method != "POST":
|
2019-12-18 14:10:30 +00:00
|
|
|
return render(request, "accounts/unsubscribe_submit.html", ctx)
|
2018-11-09 20:12:11 +00:00
|
|
|
|
2015-12-15 00:27:24 +00:00
|
|
|
user = User.objects.get(username=username)
|
2017-08-30 19:42:45 +00:00
|
|
|
profile = Profile.objects.for_user(user)
|
|
|
|
profile.reports_allowed = False
|
2017-12-29 16:03:42 +00:00
|
|
|
profile.next_report_date = None
|
2017-10-14 13:03:56 +00:00
|
|
|
profile.nag_period = td()
|
2017-12-29 16:03:42 +00:00
|
|
|
profile.next_nag_date = None
|
2017-08-30 19:42:45 +00:00
|
|
|
profile.save()
|
2015-12-15 00:27:24 +00:00
|
|
|
|
|
|
|
return render(request, "accounts/unsubscribed.html")
|
2016-05-09 12:35:13 +00:00
|
|
|
|
|
|
|
|
2017-03-16 17:39:30 +00:00
|
|
|
@require_POST
|
|
|
|
@login_required
|
|
|
|
def close(request):
|
|
|
|
user = request.user
|
|
|
|
|
2020-02-03 09:11:21 +00:00
|
|
|
# Cancel their subscription:
|
2017-03-16 17:39:30 +00:00
|
|
|
sub = Subscription.objects.filter(user=user).first()
|
|
|
|
if sub:
|
2020-02-03 09:11:21 +00:00
|
|
|
sub.cancel()
|
2017-03-16 17:39:30 +00:00
|
|
|
|
|
|
|
user.delete()
|
|
|
|
|
2017-05-28 10:38:38 +00:00
|
|
|
# Deleting user also deletes its profile, checks, channels etc.
|
|
|
|
|
2017-03-16 17:39:30 +00:00
|
|
|
request.session.flush()
|
|
|
|
return redirect("hc-index")
|
2019-01-28 18:09:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
@require_POST
|
|
|
|
@login_required
|
|
|
|
def remove_project(request, code):
|
2019-01-29 17:57:18 +00:00
|
|
|
project = get_object_or_404(Project, code=code, owner=request.user)
|
2019-01-28 18:09:23 +00:00
|
|
|
project.delete()
|
2019-01-29 17:57:18 +00:00
|
|
|
return redirect("hc-index")
|