From 9401bc3987fd45c8530a75da88a62e74e22dd565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Mon, 16 Nov 2020 16:22:25 +0200 Subject: [PATCH] Update the "Close Account" function to use confirmation codes --- hc/accounts/tests/test_close_account.py | 47 ++++++++++++++++++----- hc/accounts/views.py | 25 ++++++++----- templates/accounts/close_account.html | 50 +++++++++++++++++++++++++ templates/accounts/profile.html | 41 ++++---------------- 4 files changed, 111 insertions(+), 52 deletions(-) create mode 100644 templates/accounts/close_account.html diff --git a/hc/accounts/tests/test_close_account.py b/hc/accounts/tests/test_close_account.py index e83fb827..3306f068 100644 --- a/hc/accounts/tests/test_close_account.py +++ b/hc/accounts/tests/test_close_account.py @@ -7,6 +7,21 @@ from hc.test import BaseTestCase class CloseAccountTestCase(BaseTestCase): + def test_it_requires_sudo_mode(self): + self.client.login(username="alice@example.org", 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="alice@example.org", 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="alice@example.org", password="password") - r = self.client.post("/accounts/close/") - self.assertEqual(r.status_code, 302) + self.set_sudo_flag() + + payload = {"confirmation": "alice@example.org"} + 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="alice@example.org", 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="bob@example.org", password="password") - r = self.client.post("/accounts/close/") - self.assertEqual(r.status_code, 302) + self.set_sudo_flag() + + payload = {"confirmation": "bob@example.org"} + 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="bob@example.org", password="password") - r = self.client.get("/accounts/close/") - self.assertEqual(r.status_code, 405) diff --git a/hc/accounts/views.py b/hc/accounts/views.py index 8c8e5eec..1c07766f 100644 --- a/hc/accounts/views.py +++ b/hc/accounts/views.py @@ -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 diff --git a/templates/accounts/close_account.html b/templates/accounts/close_account.html new file mode 100644 index 00000000..a2906a67 --- /dev/null +++ b/templates/accounts/close_account.html @@ -0,0 +1,50 @@ +{% extends "base.html" %} +{% load compress static hc_extras %} + +{% block content %} + +
+
+ {% csrf_token %} +
+
+

Close Account?

+

+ +

+ You are about to close your {% site_name %} account. This + operation will permanently remove: +

+ +
    +
  • {{ request.user.project_set.count }} project{{ request.user.project_set.count|pluralize }}
  • +
  • {{ request.profile.num_checks_used }} check{{ request.profile.num_checks_used|pluralize }}
  • +
+ +

+ To confirm, please type your account's email address + ({{ request.user.email }}) below: +

+ +
+ +
+ +
+ Cancel + +
+
+
+
+
+{% endblock %} diff --git a/templates/accounts/profile.html b/templates/accounts/profile.html index d1b43ef4..26abad7c 100644 --- a/templates/accounts/profile.html +++ b/templates/accounts/profile.html @@ -193,45 +193,18 @@
{% csrf_token %}

Close Account

- Close Account - This will permanently remove your {{ site_name }} account +

This will permanently remove your {{ site_name }} account.

+
- -