diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 16a18f55..9dd1f0f8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,12 +7,20 @@ Changes **New features** +* Made user emails unique to prevent two users logging in with the same email, causing an error + + .. warning:: The default value for ``ELASTIC_APM_SERVICE_NAME`` changed from ``Open Notificaties - `` to ``nrc - ``. The default values for ``DB_NAME``, ``DB_USER``, ``DB_PASSWORD`` changed from ``opennotificaties`` to ``nrc``. The default value for ``LOG_OUTGOING_REQUESTS_DB_SAVE`` changed from ``False`` to ``True``. +.. warning:: + User email addresses will now be unique on a database level. The database migration will fail if there are already + two or more users with the same email address. You must ensure this is not the case before upgrading. + + Bugfixes/QoL: * Settings module was refactored to use generic settings provided by Open API Framework diff --git a/src/nrc/accounts/migrations/0005_user_filled_email_unique.py b/src/nrc/accounts/migrations/0005_user_filled_email_unique.py new file mode 100644 index 00000000..d93f78cc --- /dev/null +++ b/src/nrc/accounts/migrations/0005_user_filled_email_unique.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.11 on 2024-07-02 14:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("accounts", "0004_migrate_from_auth_adfs_db"), + ] + + operations = [ + migrations.AddConstraint( + model_name="user", + constraint=models.UniqueConstraint( + condition=models.Q(("email", ""), _negated=True), + fields=("email",), + name="filled_email_unique", + ), + ), + ] diff --git a/src/nrc/accounts/models.py b/src/nrc/accounts/models.py index 354c7e34..3fb1e560 100644 --- a/src/nrc/accounts/models.py +++ b/src/nrc/accounts/models.py @@ -1,5 +1,6 @@ from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin from django.db import models +from django.db.models import Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -44,6 +45,11 @@ class User(AbstractBaseUser, PermissionsMixin): class Meta: verbose_name = _("user") verbose_name_plural = _("users") + constraints = [ + models.UniqueConstraint( + fields=["email"], condition=~Q(email=""), name="filled_email_unique" + ) + ] def get_full_name(self): """ diff --git a/src/nrc/accounts/tests/test_user_manager.py b/src/nrc/accounts/tests/test_user_manager.py index 1a6ab7d2..3f4e0c90 100644 --- a/src/nrc/accounts/tests/test_user_manager.py +++ b/src/nrc/accounts/tests/test_user_manager.py @@ -1,3 +1,4 @@ +from django.db import IntegrityError from django.test import TestCase from ..models import User @@ -20,3 +21,15 @@ def test_create_user(self): self.assertFalse(user.is_superuser) self.assertFalse(user.is_staff) self.assertFalse(user.has_usable_password()) + + def test_create_users_with_same_email(self): + User.objects.create(username="AAA", email="aaa@aaa.aaa", password="aaa!") + + with self.assertRaises(IntegrityError): + User.objects.create(username="BBB", email="aaa@aaa.aaa", password="bbb!") + + def test_create_user_with_blank_emails(self): + User.objects.create(username="AAA", email="", password="aaa!") + User.objects.create(username="BBB", email="", password="bbb!") + + self.assertEqual(User.objects.count(), 2)