diff --git a/src/backend/InvenTree/InvenTree/auth_overrides.py b/src/backend/InvenTree/InvenTree/auth_overrides.py index bf590d0500..b695f977f8 100644 --- a/src/backend/InvenTree/InvenTree/auth_overrides.py +++ b/src/backend/InvenTree/InvenTree/auth_overrides.py @@ -149,6 +149,7 @@ class RegistrationMixin: def save_user(self, request, user, form, commit=True): """Check if a default group is set in settings.""" + user._is_registering = True # marker for email synchronization # Create the user user = super().save_user(request, user, form) diff --git a/src/backend/InvenTree/part/test_notification_stale.py b/src/backend/InvenTree/part/test_notification_stale.py index 7999ab6ed5..5aacfc45d9 100644 --- a/src/backend/InvenTree/part/test_notification_stale.py +++ b/src/backend/InvenTree/part/test_notification_stale.py @@ -3,8 +3,6 @@ from datetime import timedelta from unittest.mock import patch -from allauth.account.models import EmailAddress - import part.models import part.tasks import stock.models @@ -24,9 +22,6 @@ class StaleStockNotificationTests(InvenTreeTestCase): """Create test data as part of initialization.""" super().setUpTestData() - # Add email address for user - EmailAddress.objects.create(user=cls.user, email='test@testing.com') - # Create test parts cls.part1 = part.models.Part.objects.create( name='Test Part 1', diff --git a/src/backend/InvenTree/users/models.py b/src/backend/InvenTree/users/models.py index a9f41f241a..5b922c1e19 100644 --- a/src/backend/InvenTree/users/models.py +++ b/src/backend/InvenTree/users/models.py @@ -46,28 +46,6 @@ User.add_to_class('__str__', user_model_str) # Overriding User.__str__ # OVERRIDE END -if settings.LDAP_AUTH: - from django_auth_ldap.backend import populate_user # ty: ignore[unresolved-import] - - @receiver(populate_user) - def create_email_address(user, **kwargs): - """If a django user is from LDAP and has an email attached to it, create an allauth email address for them automatically. - - https://django-auth-ldap.readthedocs.io/en/latest/users.html#populating-users - https://django-auth-ldap.readthedocs.io/en/latest/reference.html#django_auth_ldap.backend.populate_user - """ - # User must exist in the database before we can create their EmailAddress. By their recommendation, - # we can just call .save() now - user.save() - - # if they got an email address from LDAP, create it now and make it the primary - if ( - user.email - and not EmailAddress.objects.filter(user=user, email=user.email).exists() - ): - EmailAddress.objects.create(user=user, email=user.email, primary=True) - - def default_token(): """Generate a default value for the token.""" return ApiToken.generate_key() @@ -647,3 +625,30 @@ def validate_primary_group_on_group_change(sender, instance, action, **kwargs): if profile.primary_group and profile.primary_group not in instance.groups.all(): profile.primary_group = None profile.save() + + +# update allauth user mail +@receiver(post_save, sender=User) +def sync_user_email_address(sender, instance: User, created: bool, **kwargs): + """Keep the allauth EmailAddress in sync with User email field.""" + # Are we currently in the API path of user registration? + if getattr(instance, '_is_registering', False): + return + + if isImportingData() or isReadOnlyCommand(): + return + + if not instance.email: + return + + primary_address = EmailAddress.objects.filter(user=instance, primary=True).first() + + if primary_address: + if primary_address.email != instance.email: + primary_address.email = instance.email + primary_address.verified = False + primary_address.save() + elif not EmailAddress.objects.filter(user=instance, email=instance.email).exists(): + EmailAddress.objects.create( + user=instance, email=instance.email, primary=True, verified=False + ) diff --git a/src/backend/InvenTree/users/test_api.py b/src/backend/InvenTree/users/test_api.py index e5440bc93e..12fab15ff2 100644 --- a/src/backend/InvenTree/users/test_api.py +++ b/src/backend/InvenTree/users/test_api.py @@ -6,6 +6,8 @@ from unittest import mock from django.contrib.auth.models import Group, User from django.urls import reverse +from allauth.account.models import EmailAddress + from InvenTree.unit_test import InvenTreeAPITestCase from users.models import ApiToken from users.ruleset import RULESET_NAMES, get_ruleset_models @@ -370,6 +372,26 @@ class SuperuserAPITests(InvenTreeAPITestCase): resp = self.put(url, {'password': 'inventree'}, expected_code=200) self.assertEqual(resp.data, {}) + def test_email_address_sync_signal(self): + """Test emailadress sync.""" + user = User.objects.create(username='start', email='start@example.org') + self.assertTrue( + EmailAddress.objects.filter( + user=user, email='start@example.org', primary=True + ).exists() + ) + + # change should trigger emailaddress update + user.email = 'updated@example.org' + user.save() + + self.assertFalse( + EmailAddress.objects.filter(user=user, email='start@example.org').exists() + ) + updated = EmailAddress.objects.get(user=user, primary=True) + self.assertEqual(updated.email, 'updated@example.org') + self.assertFalse(updated.verified) + class UserTokenTests(InvenTreeAPITestCase): """Tests for user token functionality."""