diff --git a/src/djangooidc/backends.py b/src/djangooidc/backends.py index ac65c89d4..08ca47d2e 100644 --- a/src/djangooidc/backends.py +++ b/src/djangooidc/backends.py @@ -49,6 +49,8 @@ class OpenIdConnectBackend(ModelBackend): user, created = UserModel.objects.update_or_create(**args) if created: user = self.configure_user(user, **kwargs) + # run a newly created user's callback for a first-time login + user.first_login() else: try: user = UserModel.objects.get_by_natural_key(username) diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 97f5753b3..a3ab68511 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -1,6 +1,8 @@ from django.contrib.auth.models import AbstractUser from django.db import models +from .domain_invitation import DomainInvitation + from phonenumber_field.modelfields import PhoneNumberField # type: ignore @@ -31,3 +33,14 @@ class User(AbstractUser): return self.email else: return self.username + + def first_login(self): + """Callback when the user is authenticated for the very first time. + + When a user first arrives on the site, we need to retrieve any domain + invitations that match their email address. + """ + for invitation in DomainInvitation.objects.filter( + email=self.email, status=DomainInvitation.SENT + ): + invitation.retrieve() diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index e36390e56..b60898e50 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -193,6 +193,11 @@ class TestInvitations(TestCase): with self.assertRaises(RuntimeError): self.invitation.retrieve() + def test_retrieve_on_first_login(self): + """A new user's first_login callback retrieves their invitations.""" + self.user.first_login() + self.assertTrue(UserDomainRole.objects.get(user=self.user, domain=self.domain)) + @skip("Not implemented yet.") class TestDomainApplicationLifeCycle(TestCase): diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 8f7a7e54e..7f3dc6429 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -1196,3 +1196,28 @@ class TestDomainDetail(TestWithDomainPermissions, WebTest): self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id})) with self.assertRaises(DomainInvitation.DoesNotExist): DomainInvitation.objects.get(id=invitation.id) + + @boto3_mocking.patching + def test_domain_invitation_flow(self): + """Send an invitation to a new user, log in and load the dashboard.""" + EMAIL = "mayor@igorville.gov" + User.objects.filter(email=EMAIL).delete() + + add_page = self.app.get( + reverse("domain-users-add", kwargs={"pk": self.domain.id}) + ) + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + add_page.form["email"] = EMAIL + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + add_page.form.submit() + + # user was invited, create them + new_user = User.objects.create(username=EMAIL, email=EMAIL) + # log them in to `self.app` + self.app.set_user(new_user.username) + # and manually call the first login callback + new_user.first_login() + + # Now load the home page and make sure our domain appears there + home_page = self.app.get(reverse("home")) + self.assertContains(home_page, self.domain.name)