diff --git a/gregui/authentication/auth_backends.py b/gregui/authentication/auth_backends.py index 63754aae3e4591fc39d840cacca7c1226bda4460..c3ff9ef0afdea9ade3a7c77ab5ab29296f08b1f2 100644 --- a/gregui/authentication/auth_backends.py +++ b/gregui/authentication/auth_backends.py @@ -10,13 +10,13 @@ from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.backends import BaseBackend, UserModel from django.core.exceptions import SuspiciousOperation -from django.utils.timezone import make_aware +from django.utils import timezone from mozilla_django_oidc.auth import OIDCAuthenticationBackend from greg.models import Identity, Person, Sponsor, InvitationLink from gregui.models import GregUserProfile -from gregui.utils import name_diff +from gregui.utils import name_diff_too_large logger = structlog.getLogger(__name__) @@ -339,7 +339,7 @@ class GregOIDCBackend(ValidatingOIDCBackend): existing_nin.source = source existing_nin.value = new_nin existing_nin.verified = Identity.Verified.AUTOMATIC - existing_nin.verified_at = make_aware(datetime.datetime.now()) + existing_nin.verified_at = timezone.make_aware(datetime.datetime.now()) existing_nin.save() logger.info("identity_update", identity_id=existing_nin.id) @@ -357,7 +357,7 @@ class GregOIDCBackend(ValidatingOIDCBackend): type=Identity.IdentityType.NORWEGIAN_NATIONAL_ID_NUMBER, value=new_nin, verified=Identity.Verified.AUTOMATIC, - verified_at=make_aware(datetime.datetime.now()), + verified_at=timezone.make_aware(datetime.datetime.now()), ) nin_id.save() logger.info("identity_add", identity_id=nin_id.id) @@ -399,7 +399,7 @@ class GregOIDCBackend(ValidatingOIDCBackend): existing_feide_id.source = source existing_feide_id.value = new_feide_id existing_feide_id.verified = Identity.Verified.AUTOMATIC - existing_feide_id.verified_at = make_aware(datetime.datetime.now()) + existing_feide_id.verified_at = timezone.make_aware(datetime.datetime.now()) existing_feide_id.save() else: @@ -421,7 +421,7 @@ class GregOIDCBackend(ValidatingOIDCBackend): type=Identity.IdentityType.FEIDE_ID, value=new_feide_id, verified=Identity.Verified.AUTOMATIC, - verified_at=make_aware(datetime.datetime.now()), + verified_at=timezone.make_aware(datetime.datetime.now()), ) feide_id.save() logger.info("identity_add", identity_id=feide_id.id, feide_id=new_feide_id) @@ -445,7 +445,7 @@ class GregOIDCBackend(ValidatingOIDCBackend): old_email = existing_email.value existing_email.value = new_email existing_email.verified = Identity.Verified.AUTOMATIC - existing_email.verified_at = make_aware(datetime.datetime.now()) + existing_email.verified_at = timezone.make_aware(datetime.datetime.now()) existing_email.save() logger.info( "identity_update", @@ -472,19 +472,19 @@ class GregOIDCBackend(ValidatingOIDCBackend): type=Identity.IdentityType.FEIDE_EMAIL, value=new_email, verified=Identity.Verified.AUTOMATIC, - verified_at=make_aware(datetime.datetime.now()), + verified_at=timezone.make_aware(datetime.datetime.now()), ) email_id.save() logger.info("identity_add", identity_id=email_id.id, value=new_email) def _update_person_name( - self, person: Person, first_name: str, last_name: str, source: str + self, person: Person, first_name: str, last_name: str ) -> None: new_combined_name = f"{first_name} {last_name}" existing_combined_name = f"{person.first_name} {person.last_name}" - if name_diff(existing_combined_name, new_combined_name) > 4: + if name_diff_too_large(existing_combined_name, new_combined_name, 4): logger.warning( "name_diff_to_large", existing_name=existing_combined_name, @@ -518,9 +518,7 @@ class GregOIDCBackend(ValidatingOIDCBackend): self._update_person_feide_id(person, userinfo["userid_feide"], "feide") if "email" in userinfo: self._update_person_email(person, userinfo["email"], "feide") - self._update_person_name( - person, userinfo["first_name"], userinfo["last_name"], "feide" - ) + self._update_person_name(person, userinfo["first_name"], userinfo["last_name"]) def _update_sponsor_from_userinfo(self, userinfo: dict, sponsor: Sponsor) -> None: """Updates a sponsor object with data from OIDC.""" @@ -578,26 +576,19 @@ class GregOIDCBackend(ValidatingOIDCBackend): def _get_or_create_greg_user_profile(self, userinfo: dict, user: UserModel): """Get or create a GregUserProfile.""" - def _get_invite_person( - session, old_person: Optional[Person] - ) -> Optional[Person]: + def _get_invitation_link(session) -> Optional[InvitationLink]: try: - invitation_link = InvitationLink.objects.get(uuid=session["invite_id"]) + return InvitationLink.objects.get(uuid=session["invite_id"]) except InvitationLink.DoesNotExist: logger.error("invite_not_found", invite_id=session["invite_id"]) logger.bind(authenticating_invite=False) - return old_person + return None + def _get_invite_person(invitation_link) -> Person: logger.bind(authenticating_invite=True) role = invitation_link.invitation.role invite_person = role.person logger.info("invite_found", invite_person=invite_person.id) - - if old_person and not old_person == invite_person: - role.person = old_person - role.save() - invite_person.delete() - return old_person return invite_person logger.bind(user=user) @@ -618,7 +609,33 @@ class GregOIDCBackend(ValidatingOIDCBackend): old_person = None if user_profile and user_profile.person: old_person = self._get_person_from_userinfo(userinfo) - person = _get_invite_person(session, old_person) + person = old_person + invitation_link = _get_invitation_link(session) + if invitation_link: + inv_person = _get_invite_person(invitation_link) + if old_person and old_person != inv_person: + # Logged in person has a person object in greg but has followed an + # invite. + inv_name = f"{inv_person.first_name} {inv_person.last_name}" + old_name = f"{old_person.first_name} {old_person.last_name}" + if ( + not inv_person.registration_completed_date + and not name_diff_too_large(old_name, inv_name, 4) + ): + # The name is close and the invited person has not completed + # registration. Give the role to the existing person, and + # delete the invited one. + role = invitation_link.invitation.role + role.person = old_person + role.save() + inv_person.delete() + else: + # The logged in user has gotten someone else's invitation, and + # the invitation should be disabled. + invitation_link.expire = timezone.now() + invitation_link.save() + else: + person = inv_person else: person = self._get_person_from_userinfo(userinfo) logger.bind(authenticating_invite=False) diff --git a/gregui/tests/test_utils.py b/gregui/tests/test_utils.py index f759f30611ca456c534bb17cb10994356f23469c..d8cfe5311159be3e68d1999aaaf6ecf4cda594ef 100644 --- a/gregui/tests/test_utils.py +++ b/gregui/tests/test_utils.py @@ -1,4 +1,4 @@ -from gregui.utils import name_diff, restricted_damarau_levenshtein +from gregui.utils import name_diff, name_diff_too_large, restricted_damarau_levenshtein def test_rdm_replace(): @@ -21,3 +21,9 @@ def test_name_longer(): def test_name_threshold(): """Verify continues with raised threshold""" assert name_diff("Abcdefgh Ijklmnop", "Qrstuvw Xyz", 100) == 10 + + +def test_name_too_large(): + """Verify boolean version responds with expected boolean""" + assert not name_diff_too_large("Test Name", "Testing Name", 3) + assert name_diff_too_large("Test Name", "Testing Name", 2) diff --git a/gregui/utils.py b/gregui/utils.py index b08012f78fd646377d29955b6770d2be7790d0aa..0b19c993a6996d436139bd1d0e16b7191e60728b 100644 --- a/gregui/utils.py +++ b/gregui/utils.py @@ -60,3 +60,7 @@ def name_diff(full_name1: str, full_name2: str, threshold: int = 2) -> int: if total_difference > threshold: break return total_difference + + +def name_diff_too_large(full_name1: str, full_name2: str, threshold: int) -> bool: + return name_diff(full_name1, full_name2, threshold) > threshold