Browse Source

Update the "Close Account" function to use confirmation codes

pull/456/head
Pēteris Caune 4 years ago
parent
commit
9401bc3987
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
4 changed files with 111 additions and 52 deletions
  1. +38
    -9
      hc/accounts/tests/test_close_account.py
  2. +16
    -9
      hc/accounts/views.py
  3. +50
    -0
      templates/accounts/close_account.html
  4. +7
    -34
      templates/accounts/profile.html

+ 38
- 9
hc/accounts/tests/test_close_account.py View File

@ -7,6 +7,21 @@ from hc.test import BaseTestCase
class CloseAccountTestCase(BaseTestCase):
def test_it_requires_sudo_mode(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get("/accounts/close/")
self.assertContains(r, "We have sent a confirmation code")
def test_it_shows_confirmation_form(self):
self.client.login(username="[email protected]", password="password")
self.set_sudo_flag()
r = self.client.get("/accounts/close/")
self.assertContains(r, "Close Account?")
self.assertContains(r, "1 project")
self.assertContains(r, "0 checks")
@patch("hc.payments.models.braintree")
def test_it_works(self, mock_braintree):
Check.objects.create(project=self.project, tags="foo a-B_1 baz@")
@ -15,8 +30,11 @@ class CloseAccountTestCase(BaseTestCase):
)
self.client.login(username="[email protected]", password="password")
r = self.client.post("/accounts/close/")
self.assertEqual(r.status_code, 302)
self.set_sudo_flag()
payload = {"confirmation": "[email protected]"}
r = self.client.post("/accounts/close/", payload)
self.assertRedirects(r, "/")
# Alice should be gone
alices = User.objects.filter(username="alice")
@ -31,10 +49,26 @@ class CloseAccountTestCase(BaseTestCase):
# Subscription should be gone
self.assertFalse(Subscription.objects.exists())
def test_it_requires_confirmation(self):
self.client.login(username="[email protected]", password="password")
self.set_sudo_flag()
payload = {"confirmation": "incorrect"}
r = self.client.post("/accounts/close/", payload)
self.assertContains(r, "Close Account?")
self.assertContains(r, "has-error")
# Alice should be still present
self.alice.refresh_from_db()
self.profile.refresh_from_db()
def test_partner_removal_works(self):
self.client.login(username="[email protected]", password="password")
r = self.client.post("/accounts/close/")
self.assertEqual(r.status_code, 302)
self.set_sudo_flag()
payload = {"confirmation": "[email protected]"}
r = self.client.post("/accounts/close/", payload)
self.assertRedirects(r, "/")
# Alice should be still present
self.alice.refresh_from_db()
@ -43,8 +77,3 @@ class CloseAccountTestCase(BaseTestCase):
# Bob should be gone
bobs = User.objects.filter(username="bob")
self.assertFalse(bobs.exists())
def test_it_rejects_get(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get("/accounts/close/")
self.assertEqual(r.status_code, 405)

+ 16
- 9
hc/accounts/views.py View File

@ -540,22 +540,29 @@ def unsubscribe_reports(request, signed_username):
return render(request, "accounts/unsubscribed.html")
@require_POST
@login_required
@require_sudo_mode
def close(request):
user = request.user
# Cancel their subscription:
sub = Subscription.objects.filter(user=user).first()
if sub:
sub.cancel()
if request.method == "POST":
if request.POST.get("confirmation") == request.user.email:
# Cancel their subscription:
sub = Subscription.objects.filter(user=user).first()
if sub:
sub.cancel()
# Deleting user also deletes its profile, checks, channels etc.
user.delete()
user.delete()
request.session.flush()
return redirect("hc-index")
# Deleting user also deletes its profile, checks, channels etc.
ctx = {}
if "confirmation" in request.POST:
ctx["wrong_confirmation"] = True
request.session.flush()
return redirect("hc-index")
return render(request, "accounts/close_account.html", ctx)
@require_POST


+ 50
- 0
templates/accounts/close_account.html View File

@ -0,0 +1,50 @@
{% extends "base.html" %}
{% load compress static hc_extras %}
{% block content %}
<div class="row">
<form class="col-sm-6 col-sm-offset-3" method="post">
{% csrf_token %}
<div class="panel panel-default">
<div class="panel-body settings-block">
<h2>Close Account?</h2>
<p></p>
<p>
You are about to close your {% site_name %} account. This
operation will permanently remove:
</p>
<ul>
<li>{{ request.user.project_set.count }} project{{ request.user.project_set.count|pluralize }}</li>
<li>{{ request.profile.num_checks_used }} check{{ request.profile.num_checks_used|pluralize }}</li>
</ul>
<p>
To confirm, please type your account's email address
(<code>{{ request.user.email }}</code>) below:
</p>
<div class="form-group {% if wrong_confirmation%}has-error{% endif %}">
<input
class="form-control"
type="text"
name="confirmation"
placeholder="type your email address to confirm" />
</div>
<div class="text-right">
<a
href="{% url 'hc-profile' %}"
class="btn btn-default">Cancel</a>
<button
type="submit"
name="close_account"
class="btn btn-danger">Close Account</button>
</div>
</div>
</div>
</form>
</div>
{% endblock %}

+ 7
- 34
templates/accounts/profile.html View File

@ -193,45 +193,18 @@
<div class="panel-body settings-block">
{% csrf_token %}
<h2>Close Account</h2>
<a
id="close-account"
href="#"
class="btn btn-default pull-right"
data-toggle="modal"
data-target="#close-account-modal">Close Account</a>
This will permanently remove your {{ site_name }} account
<p>This will permanently remove your {{ site_name }} account.</p>
<div class="text-right">
<a
id="close-account"
href="{% url 'hc-close' %}"
class="btn btn-default">Close Account</a>
</div>
</div>
</div>
</div>
</div>
<div id="close-account-modal" class="modal">
<div class="modal-dialog">
<form method="post" action="{% url 'hc-close' %}">
{% csrf_token %}
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4>Close Account?</h4>
</div>
<div class="modal-body">
<p></p>
<p>You are about to permanently remove
the account <strong>{{ request.user.email }}</strong> and all
of its associated projects, checks and integrations. Are you sure?
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button
type="submit"
class="btn btn-danger">Close Account</button>
</div>
</div>
</form>
</div>
</div>
<div id="leave-project-modal" class="modal">
<div class="modal-dialog">
<form id="leave-project-form" method="post">


Loading…
Cancel
Save