import datetime import time import logging from typing import Callable, Tuple, Optional import pytest from django.contrib.auth import get_user_model from django.contrib.auth.backends import UserModel from django.utils.timezone import make_aware from rest_framework.authtoken.admin import User from rest_framework.test import APIClient from greg.models import ( ConsentType, ConsentChoice, Identity, Invitation, InvitationLink, OrganizationalUnit, Person, Role, RoleType, Sponsor, ) from gregui.models import EmailTemplate, GregUserProfile # faker spams the logs with localisation warnings # see https://github.com/joke2k/faker/issues/753 logging.getLogger("faker").setLevel(logging.ERROR) # OIDC stuff @pytest.fixture def claims(): return { "sub": "subsub", "connect-userid_sec": ["feide:frank_foreleser@spusers.feide.no"], "dataporten-userid_sec": [ # "feide:frank_foreleser@spusers.feide.no" ], "name": "Frank Foreleser Føllesen", "email": "noreply@feide.no", "email_verified": True, "picture": "https://api.dataporten.no/userinfo/v1/user/media/p:2192dff7-6989-4244-83cc-ae5e78875bdd", } @pytest.fixture def id_token_payload(): return { "iss": "https://auth.dataporten.no", "jti": "jtijti", "aud": "lalalalala", "sub": "subsub", "iat": 1605174731, "exp": 1605178331, "auth_time": 1605174731, "nonce": "noncenonce", } @pytest.fixture def client() -> APIClient: client = APIClient() return client @pytest.fixture def unit_foo() -> OrganizationalUnit: ou = OrganizationalUnit.objects.create(name_en="Foo EN", name_nb="Foo NB") return OrganizationalUnit.objects.get(id=ou.id) @pytest.fixture def role_type_foo() -> RoleType: rt = RoleType.objects.create( identifier="role_foo", name_en="Role Foo EN", name_nb="Role Foo NB" ) return RoleType.objects.get(id=rt.id) @pytest.fixture def create_sponsor() -> Callable[[str, str, str, OrganizationalUnit], Sponsor]: def create_sponsor(feide_id, first_name, last_name, unit, work_email=None): sponsor = Sponsor( feide_id=feide_id, first_name=first_name, last_name=last_name, work_email=work_email, ) sponsor.save() sponsor.units.add(unit, through_defaults={"hierarchical_access": False}) sponsor.save() return Sponsor.objects.get(id=sponsor.id) return create_sponsor @pytest.fixture def sponsor_foo_data() -> dict: return dict( feide_id="foo@example.org", first_name="Sponsor", last_name="Bar", work_email="foo@example.org", ) @pytest.fixture def sponsor_foo( unit_foo: OrganizationalUnit, sponsor_foo_data, create_sponsor ) -> Sponsor: return create_sponsor(**sponsor_foo_data, unit=unit_foo) @pytest.fixture def sponsor_bar(unit_foo: OrganizationalUnit, create_sponsor) -> Sponsor: return create_sponsor( feide_id="bar@example.com", first_name="Bar", last_name="Baz", unit=unit_foo ) @pytest.fixture def create_user() -> Callable[[str, str, str, str], UserModel]: user_model = get_user_model() def create_user( username: str, first_name: str = "", last_name: str = "", email: str = "" ): user = user_model.objects.create( username=username, email=email, first_name=first_name, last_name=last_name, ) return user_model.objects.get(id=user.id) return create_user @pytest.fixture def user_sponsor(sponsor_foo: Sponsor, create_user) -> User: user_model = get_user_model() # Create a user and link him to a sponsor user = create_user( username="test_sponsor", email="test@example.org", first_name="Test", last_name="Sponsor", ) GregUserProfile.objects.create(user=user, sponsor=sponsor_foo) # This user is a sponsor for unit_foo return user_model.objects.get(id=user.id) @pytest.fixture def user_person(person_foo: Sponsor, create_user) -> User: user_model = get_user_model() # Create a user and link him to a sponsor user = create_user( username="test_person", email="person@example.org", first_name="Test", last_name="Person", ) GregUserProfile.objects.create(user=user, person=person_foo) # This user is a sponsor for unit_foo return user_model.objects.get(id=user.id) @pytest.fixture def create_greg_user_profile() -> Callable[ [UserModel, Optional[Person], Optional[Sponsor]], GregUserProfile ]: user_model = get_user_model() def create_greg_user_profile( user: user_model, person: Optional[Person] = None, sponsor: Optional[Sponsor] = None, ): user_profile = GregUserProfile( user=user, person=person, sponsor=sponsor, ) return GregUserProfile.objects.get(id=user_profile.id) return create_greg_user_profile @pytest.fixture def create_role() -> Callable[[Person, Sponsor, OrganizationalUnit, RoleType], Role]: """Returns a function for creating roles.""" def create_role( person: Person, sponsor: Sponsor, unit: OrganizationalUnit, role_type: RoleType ) -> Role: role = Role( person=person, sponsor=sponsor, orgunit=unit, end_date="2050-10-15", type=role_type, ) role.save() return Role.objects.get(id=role.id) return create_role @pytest.fixture def role(person_invited, sponsor_foo, unit_foo, role_type_foo, create_role) -> Role: return create_role(person_invited, sponsor_foo, unit_foo, role_type_foo) @pytest.fixture def create_invitation() -> Callable[[Role], Invitation]: """Returns a function for creating invitations.""" def create_invitation(role: Role) -> Invitation: invitation = Invitation(role=role) invitation.save() return Invitation.objects.get(id=invitation.id) return create_invitation @pytest.fixture def invitation(role, create_invitation) -> Invitation: return create_invitation(role) @pytest.fixture def invitation_valid_date() -> datetime.datetime: return make_aware(datetime.datetime(2060, 10, 15)) @pytest.fixture def invitation_expired_date() -> datetime.datetime: return make_aware(datetime.datetime(1970, 1, 1)) @pytest.fixture def create_invitation_link( invitation_valid_date, ) -> Callable[[Invitation, datetime.datetime], InvitationLink]: """Returns a function for creating invitation links.""" def create_invitation_link( invitation: Invitation, expire_date: datetime.datetime = invitation_valid_date ) -> InvitationLink: invitation_link = InvitationLink( invitation=invitation, expire=expire_date, ) invitation_link.save() return InvitationLink.objects.get(id=invitation_link.id) return create_invitation_link @pytest.fixture def invitation_link( invitation, invitation_valid_date, create_invitation_link ) -> InvitationLink: return create_invitation_link( invitation=invitation, expire_date=invitation_valid_date, ) @pytest.fixture def invitation_link_expired( invitation, invitation_expired_date, create_invitation_link ) -> InvitationLink: return create_invitation_link( invitation=invitation, expire_date=invitation_expired_date, ) @pytest.fixture def create_person() -> Callable[[str, str, str, Optional[str], Optional[str]], Person]: # TODO fix the typing... def create_person( first_name: str, last_name: str, email: str = None, nin: str = None, feide_id: str = None, ) -> Person: person = Person.objects.create( first_name=first_name, last_name=last_name, ) if nin: Identity.objects.create( type=Identity.IdentityType.NORWEGIAN_NATIONAL_ID_NUMBER, value=nin, person=person, ) if feide_id: Identity.objects.create( type=Identity.IdentityType.FEIDE_ID, value=feide_id, person=person, ) if email: Identity.objects.create( type=Identity.IdentityType.PRIVATE_EMAIL, value=email, person=person, ) return Person.objects.get(id=person.id) return create_person @pytest.fixture def person_foo_data() -> dict: return dict( first_name="Foo", last_name="Bar", email="foo@bar.com", feide_id="bar@baz.org", nin="12345612345", ) @pytest.fixture def person_foo(create_person) -> Person: person = create_person( first_name="Foo", last_name="Bar", email="foo@bar.com", feide_id="bar@baz.org", nin="12345612345", ) return Person.objects.get(id=person.id) @pytest.fixture def person_invited(create_person) -> Person: """Invited person before registration.""" person = create_person(first_name="Foo", last_name="Bar", email="foo@example.org") return Person.objects.get(id=person.id) @pytest.fixture def invited_person( create_role, create_invitation, create_invitation_link, person_invited, sponsor_foo, unit_foo, role_type_foo, ) -> Tuple[Person, InvitationLink]: """ Invited person """ role = create_role( person=person_invited, sponsor=sponsor_foo, unit=unit_foo, role_type=role_type_foo, ) invitation = create_invitation(role=role) invitation_link = create_invitation_link(invitation=invitation) return Person.objects.get(id=person_invited.id), InvitationLink.objects.get( id=invitation_link.id ) @pytest.fixture def invited_person_no_ids( create_person, create_role, create_invitation, create_invitation_link, sponsor_foo, unit_foo, role_type_foo, ) -> Tuple[Person, InvitationLink]: """ Invited person, with no ids. """ person = create_person( first_name="foo", last_name="bar", email="foo@bar.com", ) role = create_role( person=person, sponsor=sponsor_foo, unit=unit_foo, role_type=role_type_foo ) invitation = create_invitation(role=role) invitation_link = create_invitation_link(invitation=invitation) return Person.objects.get(id=person.id), InvitationLink.objects.get( id=invitation_link.id ) @pytest.fixture def invited_person_verified_nin( create_person, create_role, create_invitation, create_invitation_link, sponsor_foo, unit_foo, role_type_foo, ) -> Tuple[Person, InvitationLink]: """ Invited person, with a verified NIN. """ person = create_person( first_name="Victor", last_name="Verified", email="foo@bar2.com", nin="12345678912", ) fnr = person.identities.get(type=Identity.IdentityType.NORWEGIAN_NATIONAL_ID_NUMBER) fnr.verified = Identity.Verified.AUTOMATIC fnr.save() role = create_role( person=person, sponsor=sponsor_foo, unit=unit_foo, role_type=role_type_foo ) invitation = create_invitation(role=role) invitation_link = create_invitation_link(invitation=invitation) return Person.objects.get(id=person.id), InvitationLink.objects.get( id=invitation_link.id ) @pytest.fixture def log_in(client) -> Callable[[UserModel], APIClient]: def _log_in(user): client.force_login(user=user) # It seems like the session was not updated automatically this way session = client.session session["oidc_id_token_payload"] = {"iat": time.time()} session.save() return client return _log_in @pytest.fixture def confirmation_template(): et = EmailTemplate.objects.create( template_key=EmailTemplate.EmailType.SPONSOR_CONFIRMATION, subject="confirmation subject", body="""Dette er en automatisk generert melding fra gjesteregistreringstjenesten. Din gjest, {{ guest }}, har fullført registrering, bekreft gjesten her: {{ confirmation_link }} This message has been automatically generated by the guest registration system. Your guest, {{ guest }}, has completed their registration, please confirm the guest here: {{ confirmation_link }}""", ) return EmailTemplate.objects.get(id=et.id) @pytest.fixture def registration_template(): et = EmailTemplate.objects.create( template_key=EmailTemplate.EmailType.GUEST_REGISTRATION, subject="registration subject", body="""Dette er en automatisk generert melding fra gjesteregistreringstjenesten. Du har blitt registrert som gjest på {{ institution }} av {{ sponsor }}. For å fullføre registreringen av gjestekontoen følg denne lenken: {{ registration_link }} This message has been automatically generated by the guest registration system. You have been registered as a guest at {{ institution }} by {{ sponsor }}. To complete the registration of your guest account, please follow this link: {{ registration_link }}""", ) return EmailTemplate.objects.get(id=et.id) @pytest.fixture def consent_type_foo() -> ConsentType: type_foo = ConsentType.objects.create( identifier="foo", name_en="Foo", name_nb="Fu", name_nn="F", description_en="Description", description_nb="Beskrivelse", description_nn="Beskriving", valid_from="2018-01-20", user_allowed_to_change=True, mandatory=False, ) ConsentChoice.objects.create( consent_type=type_foo, value="yes", text_en="Yes", text_nb="Ja", text_nn="Ja", ) ConsentChoice.objects.create( consent_type=type_foo, value="no", text_en="No", text_nb="Nei", text_nn="Nei", ) return ConsentType.objects.get(id=type_foo.id) @pytest.fixture def consent_type_bar() -> ConsentType: type_bar = ConsentType.objects.create( identifier="bar", name_en="Bar", name_nb="Ba", name_nn="B", description_en="Description", description_nb="Beskrivelse", description_nn="Beskriving", valid_from="2018-01-20", user_allowed_to_change=True, mandatory=True, ) ConsentChoice.objects.create( consent_type=type_bar, value="yes", text_en="Yes", text_nb="Ja", text_nn="Ja", ) return ConsentType.objects.get(id=type_bar.id)