From 8bcc6a390b3bb9439f846c44e49b69eafc93ca10 Mon Sep 17 00:00:00 2001 From: realaravinth Date: Sat, 11 Jun 2022 18:18:44 +0530 Subject: [PATCH] feat: management command to remove unverified users --- .../commands/rm_unverified_users.py | 42 ++++++++++++++ accounts/tests.py | 57 +++++++++++++++++++ dashboard/settings.py | 1 + 3 files changed, 100 insertions(+) create mode 100644 accounts/management/commands/rm_unverified_users.py diff --git a/accounts/management/commands/rm_unverified_users.py b/accounts/management/commands/rm_unverified_users.py new file mode 100644 index 0000000..b020795 --- /dev/null +++ b/accounts/management/commands/rm_unverified_users.py @@ -0,0 +1,42 @@ +# Copyright © 2022 Aravinth Manivannan +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +from datetime import datetime, timedelta +from django.core.management.base import BaseCommand +from django.conf import settings + +from accounts.models import AccountConfirmChallenge + + +class CleanUnverifiedUsersCommand(BaseCommand): + help = "Deletes unverified users after the period specified in settings.py" + + def handle(self, *args, **options): + now = datetime.now() + delta = now - timedelta( + seconds=settings.HOSTEA["ACCOUNTS"]["MAX_VERIFICATION_TOLERANCE_PERIOD"] + ) + # delta = delta.time() + challenges = AccountConfirmChallenge.objects.filter(created_at__lt=(delta)) + self.stdout.write(self.style.SUCCESS(f"[*] Found {len(challenges)} challenges")) + for challenge in challenges: + if not challenge.owned_by.is_active: + self.stdout.write( + self.style.SUCCESS( + f"[*] Deleting user {challenge.owned_by.username}" + ) + ) + challenge.owned_by.delete() + else: + challenge.delete() diff --git a/accounts/tests.py b/accounts/tests.py index b4ef93e..3c3b560 100644 --- a/accounts/tests.py +++ b/accounts/tests.py @@ -14,12 +14,16 @@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import time + from django.contrib.auth import get_user_model from django.urls import reverse from django.test import TestCase, Client, override_settings from django.contrib.auth import authenticate +from django.conf import settings from .models import AccountConfirmChallenge +from .management.commands.rm_unverified_users import CleanUnverifiedUsersCommand class LoginTest(TestCase): @@ -140,3 +144,56 @@ class RegistrationTest(TestCase): resp = c.post(reverse("accounts.register"), msg) self.assertEqual(resp.status_code, 400) self.assertEqual(b"Email is already registered" in resp.content, True) + + +class UnverifiedAccountCleanupTets(TestCase): + """ + Tests account clean up management command + """ + + @override_settings(HOSTEA={"ACCOUNTS": {"MAX_VERIFICATION_TOLERANCE_PERIOD": 0}}) + def test_register_works(self): + """ + Tests if register works + """ + c = Client() + + username1 = "cleanup_user1" + username2 = "cleanup_user2" + + # passwords don't match + msg = { + "username": username1, + "password": "password", + "email": f"{username1}@example.com", + "confirm_password": "password", + } + + # register user + resp = c.post(reverse("accounts.register"), msg) + self.assertEqual(resp.status_code, 302) + + msg["username"] = username2 + msg["email"] = f"{username2}@example.org" + resp = c.post(reverse("accounts.register"), msg) + self.assertEqual(resp.status_code, 302) + + user1 = get_user_model().objects.get(username=username1) + user1.is_active = True + user1.save() + + if settings.HOSTEA["ACCOUNTS"]["MAX_VERIFICATION_TOLERANCE_PERIOD"] > 10: + raise Exception( + "This test requires MAX_VERIFICATION_TOLERANCE_PERIOD to be less, ideally less 10" + ) + + time.sleep(settings.HOSTEA["ACCOUNTS"]["MAX_VERIFICATION_TOLERANCE_PERIOD"] + 2) + cmd = CleanUnverifiedUsersCommand() + cmd.handle() + + self.assertEqual( + get_user_model().objects.filter(username=username1).exists(), True + ) + self.assertEqual( + get_user_model().objects.filter(username=username2).exists(), False + ) diff --git a/dashboard/settings.py b/dashboard/settings.py index 5a71070..1ccf9de 100644 --- a/dashboard/settings.py +++ b/dashboard/settings.py @@ -134,6 +134,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" HOSTEA = { "RESTRICT_NEW_INTEGRATION_INSTALLATION": True, "INSTANCE_MAINTAINER_CONTACT": "contact@hostea.example.org", + "ACCOUNTS": {"MAX_VERIFICATION_TOLERANCE_PERIOD": 60 * 60 * 24}, # in seconds } EMAIL_CONFIG = env.email("EMAIL_URL", default="smtp://admin:password@localhost:10025")