From ddaf6a6d8a39b4366833ca9639d44a19bb392284 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Fri, 18 Feb 2022 13:06:29 +0000 Subject: [PATCH 1/3] Enhance stats + add tests --- ivatar/test_views.py | 17 +++++++++++++++++ ivatar/views.py | 11 ++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/ivatar/test_views.py b/ivatar/test_views.py index 1f3ec49..50049c0 100644 --- a/ivatar/test_views.py +++ b/ivatar/test_views.py @@ -4,6 +4,7 @@ Test our views in ivatar.ivataraccount.views and ivatar.views """ # pylint: disable=too-many-lines import os +import json import django from django.test import TestCase from django.test import Client @@ -54,3 +55,19 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods expected_url="/static/img/deadbeef.png", msg_prefix="Why does an invalid hash not redirect to deadbeef?", ) + + def test_stats(self): + """ + Test incorrect digest + """ + response = self.client.get("/stats/", follow=True) + self.assertEqual(response.status_code, 200, "unable to fetch stats!") + j = json.loads(response.content) + self.assertEqual(j["users"], 1, "user count incorrect") + self.assertEqual(j["mails"], 0, "mails count incorrect") + self.assertEqual(j["openids"], 0, "openids count incorrect") + self.assertEqual(j["unconfirmed_mails"], 0, "unconfirmed mails count incorrect") + self.assertEqual( + j["unconfirmed_openids"], 0, "unconfirmed openids count incorrect" + ) + self.assertEqual(j["avatars"], 0, "avatars count incorrect") diff --git a/ivatar/views.py b/ivatar/views.py index 4a1d83c..f0ea0d9 100644 --- a/ivatar/views.py +++ b/ivatar/views.py @@ -31,6 +31,8 @@ from ivatar.settings import CACHE_RESPONSE from ivatar.settings import CACHE_IMAGES_MAX_AGE from ivatar.settings import TRUSTED_DEFAULT_URLS from .ivataraccount.models import ConfirmedEmail, ConfirmedOpenId +from .ivataraccount.models import UnconfirmedEmail, UnconfirmedOpenId +from .ivataraccount.models import Photo from .ivataraccount.models import pil_format, file_format from .utils import mm_ng @@ -444,9 +446,12 @@ class StatsView(TemplateView, JsonResponse): self, request, *args, **kwargs ): # pylint: disable=too-many-branches,too-many-statements,too-many-locals,no-self-use,unused-argument,too-many-return-statements retval = { - "users": User.objects.all().count(), - "mails": ConfirmedEmail.objects.all().count(), - "openids": ConfirmedOpenId.objects.all().count(), # pylint: disable=no-member + "users": User.objects.count(), + "mails": ConfirmedEmail.objects.count(), + "openids": ConfirmedOpenId.objects.count(), # pylint: disable=no-member + "unconfirmed_mails": UnconfirmedEmail.objects.count(), # pylint: disable=no-member + "unconfirmed_openids": UnconfirmedOpenId.objects.count(), # pylint: disable=no-member + "avatars": Photo.objects.count(), # pylint: disable=no-member } return JsonResponse(retval) From ae3c6beed4b687b0b6eae4408bc747a9ab58520d Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Tue, 3 Jan 2023 07:41:51 +0000 Subject: [PATCH 2/3] Update testing --- .gitignore | 1 + .pre-commit-config.yaml | 2 +- attic/debug_toolbar_resources.txt | 2 + attic/encryption_test.py | 49 +++++++++++++++ attic/example_mysql_config | 7 +++ ivatar/ivataraccount/test_views.py | 16 +++++ ivatar/tools/__init__.py | 0 ivatar/tools/test_views.py | 95 +++++++++++++++++++++++++++++- 8 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 attic/debug_toolbar_resources.txt create mode 100755 attic/encryption_test.py create mode 100644 attic/example_mysql_config create mode 100644 ivatar/tools/__init__.py diff --git a/.gitignore b/.gitignore index afcc9a4..9cd722f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ falko_gravatar.jpg *.egg-info dump_all*.sql dist/ +.env.local diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0bdaa26..e9643c5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: prettier files: \.(css|js|md|markdown|json) - repo: https://github.com/python/black - rev: 22.10.0 + rev: 22.12.0 hooks: - id: black - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/attic/debug_toolbar_resources.txt b/attic/debug_toolbar_resources.txt new file mode 100644 index 0000000..2c35392 --- /dev/null +++ b/attic/debug_toolbar_resources.txt @@ -0,0 +1,2 @@ +https://django-debug-toolbar.readthedocs.io/en/latest/installation.html +https://stackoverflow.com/questions/6548947/how-can-django-debug-toolbar-be-set-to-work-for-just-some-users/6549317#6549317 diff --git a/attic/encryption_test.py b/attic/encryption_test.py new file mode 100755 index 0000000..4c10295 --- /dev/null +++ b/attic/encryption_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import django +import timeit + +os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", "ivatar.settings" +) # pylint: disable=wrong-import-position +django.setup() # pylint: disable=wrong-import-position + +from ivatar.ivataraccount.models import ConfirmedEmail, APIKey +from simplecrypt import decrypt +from binascii import unhexlify + +digest = None +digest_sha256 = None + + +def get_digest_sha256(): + digest_sha256 = ConfirmedEmail.objects.first().encrypted_digest_sha256( + secret_key=APIKey.objects.first() + ) + return digest_sha256 + + +def get_digest(): + digest = ConfirmedEmail.objects.first().encrypted_digest( + secret_key=APIKey.objects.first() + ) + return digest + + +def decrypt_digest(): + return decrypt(APIKey.objects.first().secret_key, unhexlify(digest)) + + +def decrypt_digest_256(): + return decrypt(APIKey.objects.first().secret_key, unhexlify(digest_sha256)) + + +digest = get_digest() +digest_sha256 = get_digest_sha256() + +print("Encrypt digest: %s" % timeit.timeit(get_digest, number=1)) +print("Encrypt digest_sha256: %s" % timeit.timeit(get_digest_sha256, number=1)) +print("Decrypt digest: %s" % timeit.timeit(decrypt_digest, number=1)) +print("Decrypt digest_sha256: %s" % timeit.timeit(decrypt_digest_256, number=1)) diff --git a/attic/example_mysql_config b/attic/example_mysql_config new file mode 100644 index 0000000..a0504e8 --- /dev/null +++ b/attic/example_mysql_config @@ -0,0 +1,7 @@ +DATABASES['default'] = { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'libravatar', + 'USER': 'libravatar', + 'PASSWORD': 'libravatar', + 'HOST': 'localhost', +} diff --git a/ivatar/ivataraccount/test_views.py b/ivatar/ivataraccount/test_views.py index 59bb331..051f960 100644 --- a/ivatar/ivataraccount/test_views.py +++ b/ivatar/ivataraccount/test_views.py @@ -1998,4 +1998,20 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods Test if preferences page works """ + self.login() self.client.get(reverse("user_preference")) + + def test_delete_user(self): + """ + Test if deleting user profile works + """ + + self.login() + self.client.get(reverse("delete")) + response = self.client.post( + reverse("delete"), + data={"password": self.password}, + follow=True, + ) + self.assertEqual(response.status_code, 200, "Deletion worked") + self.assertEqual(User.objects.count(), 0, "No user there any more") diff --git a/ivatar/tools/__init__.py b/ivatar/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ivatar/tools/test_views.py b/ivatar/tools/test_views.py index 1c2647b..d26da7c 100644 --- a/ivatar/tools/test_views.py +++ b/ivatar/tools/test_views.py @@ -48,16 +48,109 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods password=self.password, ) - def test_check(self): + def test_check_mail(self): """ Test check page """ + self.login() response = self.client.get(reverse("tools_check")) self.assertEqual(response.status_code, 200, "no 200 ok?") + response = self.client.post( + reverse("tools_check"), + data={"mail": "test@test.com", "size": "85"}, + follow=True, + ) + + self.assertContains( + response, + 'value="test@test.com"', + 1, + 200, + "Value not set again!?", + ) + + self.assertContains( + response, + "b642b4217b34b1e8d3bd915fc65c4452", + 3, + 200, + "Wrong md5 hash!?", + ) + self.assertContains( + response, + "f660ab912ec121d1b1e928a0bb4bc61b15f5ad44d5efdc4e1c92a25e99b8e44a", + 3, + 200, + "Wrong sha256 hash!?", + ) + self.assertContains( + response, + 'value="85"', + 1, + 200, + "Size should be set based on post params!?", + ) + + def test_check_openid(self): + """ + Test check page + """ + self.login() + response = self.client.get(reverse("tools_check")) + self.assertEqual(response.status_code, 200, "no 200 ok?") + response = self.client.post( + reverse("tools_check"), + data={"openid": "https://test.com", "size": "85"}, + follow=True, + ) + + self.assertContains( + response, + 'value="https://test.com"', + 1, + 200, + "Value not set again!?", + ) + + self.assertContains( + response, + "396936bd0bf0603d6784b65d03e96dae90566c36b62661f28d4116c516524bcc", + 3, + 200, + "Wrong sha256 hash!?", + ) + self.assertContains( + response, + 'value="85"', + 1, + 200, + "Size should be set based on post params!?", + ) def test_check_domain(self): """ Test check domain page """ + self.login() response = self.client.get(reverse("tools_check_domain")) self.assertEqual(response.status_code, 200, "no 200 ok?") + response = self.client.post( + reverse("tools_check_domain"), + data={"domain": "linux-kernel.at"}, + follow=True, + ) + self.assertEqual(response.status_code, 200, "no 200 ok?") + self.assertContains( + response, + "http://avatars.linux-kernel.at", + 2, + 200, + "Not responing with right URL!?", + ) + self.assertContains( + response, + "https://avatars.linux-kernel.at", + 2, + 200, + "Not responing with right URL!?", + ) From 9478177c83ff091076d0637296ce610b078a6322 Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Tue, 24 Jan 2023 20:15:20 +0000 Subject: [PATCH 3/3] Closes issue #91 --- ivatar/ivataraccount/test_views.py | 41 ++++++++++++++++++++++++++++++ ivatar/ivataraccount/views.py | 7 +++++ 2 files changed, 48 insertions(+) diff --git a/ivatar/ivataraccount/test_views.py b/ivatar/ivataraccount/test_views.py index 051f960..0ad10c3 100644 --- a/ivatar/ivataraccount/test_views.py +++ b/ivatar/ivataraccount/test_views.py @@ -2015,3 +2015,44 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods ) self.assertEqual(response.status_code, 200, "Deletion worked") self.assertEqual(User.objects.count(), 0, "No user there any more") + + def test_confirm_already_confirmed(self): + """ + Try to confirm a mail address that has been confirmed (by another user) + """ + + # Add mail address (stays unconfirmed) + self.test_add_email() + + # Create a second user that will conflict + user2 = User.objects.create_user( + username=self.username + "1", + password=self.password, + first_name=self.first_name, + last_name=self.last_name, + ) + ConfirmedEmail.objects.create( + email=self.email, + user=user2, + ) + + # Just to be sure + self.assertEqual( + self.user.unconfirmedemail_set.first().email, + user2.confirmedemail_set.first().email, + "Mail not the same?", + ) + + # This needs to be cought + try: + self.test_confirm_email() + except AssertionError: + pass + + # Request a random page, so we can access the messages + response = self.client.get(reverse("profile")) + self.assertEqual( + str(list(response.context[0]["messages"])[0]), + "This mail address has been taken already and cannot be confirmed", + "This should return an error message!", + ) \ No newline at end of file diff --git a/ivatar/ivataraccount/views.py b/ivatar/ivataraccount/views.py index b1dba83..f993cb5 100644 --- a/ivatar/ivataraccount/views.py +++ b/ivatar/ivataraccount/views.py @@ -207,6 +207,13 @@ class ConfirmEmailView(SuccessMessageMixin, TemplateView): messages.error(request, _("Verification key does not exist")) return HttpResponseRedirect(reverse_lazy("profile")) + if ConfirmedEmail.objects.filter(email=unconfirmed.email).count() > 0: + messages.error( + request, + _("This mail address has been taken already and cannot be confirmed"), + ) + return HttpResponseRedirect(reverse_lazy("profile")) + # TODO: Check for a reasonable expiration time in unconfirmed email (confirmed_id, external_photos) = ConfirmedEmail.objects.create_confirmed_email(