diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index bf866efa5265ec601a931ea812f04c36ec741ac1..d7bf4b92e57d9bb3dbb49a2a9c870ac31e42795d 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -146,9 +146,11 @@ "cancelInvitationDescription": "Do you want to cancel the invitation?" }, "error": { + "error": "Error", "invitationCreationFailedHeader": "Failed to create invite", "errorStatusCode": "Status code: {{statusCode}} (<3>{{statusText}}</3>)", "genericServerErrorBody": "The server reported:<1>{{errorBodyText}}</1>", - "contactHelp": "Contact help through the link in the footer if the problem persists." + "contactHelp": "Contact help through the link in the footer if the problem persists.", + "unknown": "An unknown error has occurred. If the problem persists, contact support." } } diff --git a/frontend/public/locales/en/invite.json b/frontend/public/locales/en/invite.json index 70693d275ab5a2c20d9513f1854d1fa4e0643844..ac094ff19553d9360f9af1885bc5a40152839691 100644 --- a/frontend/public/locales/en/invite.json +++ b/frontend/public/locales/en/invite.json @@ -1,6 +1,10 @@ { - "description": "Please choose how you want to log in to complete your registration. The recommended way is to log in with either Feide or ID-porten. If that is not possible you can manually fill out the registration form with your passport number.", - "header": "Guest Registration", + "description": "Please choose how you want to log in to complete your registration. The recommended way is to log in with either Feide or ID-porten. If that is not possible you can manually fill out the registration form with your passport number or Norwegian national ID number.", + "header": "Guest registration", "login": "Log in with FEIDE", - "manual": "Registrate manually" + "manual": "Register manually", + "errors": { + "invalidToken": "The invitation link you followed is invalid.", + "expiredToken": "The invitation link you followed has expired. Contact your host to get a new link." + } } diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json index 290d8f21ff951f77004efb2acb04566af96c0c4f..f716a33cae4e1c12c88636a541f98ca464bffe9f 100644 --- a/frontend/public/locales/nb/common.json +++ b/frontend/public/locales/nb/common.json @@ -146,9 +146,11 @@ "cancelInvitationDescription": "Vil du kansellere invitasjonen?" }, "error": { + "error": "Feil", "invitationCreationFailedHeader": "Kunne ikke opprette invitasjon", "errorStatusCode": "Statuskode: {{statusCode}} (<3>{{statusText}}</3>)", "genericServerErrorBody": "Respons fra server:<1>{{errorBodyText}}</1>", - "contactHelp": "Kontakt hjelp via link i footer om problemet vedvarer." + "contactHelp": "Kontakt hjelp via link i footer om problemet vedvarer.", + "unknown": "En ukjent feil har oppstått. Om problemet vedvarer, kontakt brukerstøtte." } } diff --git a/frontend/public/locales/nb/invite.json b/frontend/public/locales/nb/invite.json index 6f6b73f0d316f226596aeaa894184030db619f91..8ad98cd8c87f22369f8d57a1f9fb94a1eb7cb5cd 100644 --- a/frontend/public/locales/nb/invite.json +++ b/frontend/public/locales/nb/invite.json @@ -2,5 +2,9 @@ "description": "Vennligst velg hvordan du vil logge inn for å fullføre registreringen. Den anbefalte metoden er å logge inn gjennom Feide eller ID-porten. Dersom det ikke er mulig kan du fylle ut registreringskjemaet manuelt med passnummer", "header": "Gjestetjenesten", "login": "Logg inn med FEIDE", - "manual": "Registrer manuelt" + "manual": "Registrer manuelt", + "errors": { + "invalidToken": "Denne invitasjonslenka er ugyldig.", + "expiredToken": "Denne invitasjonslenka er utløpt. Kontakt verten din for å få tilsendt en ny." + } } diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json index b7beee8b36c12ab5006cfcc88af9857989ec992d..fa5e72fc612336c67f87a8e6e89b0c79cb766f41 100644 --- a/frontend/public/locales/nn/common.json +++ b/frontend/public/locales/nn/common.json @@ -147,9 +147,11 @@ "cancelInvitationDescription": "Vil du kansellere invitasjonen?" }, "error": { + "error": "Feil", "invitationCreationFailedHeader": "Kunne ikkje opprette invitasjon", "errorStatusCode": "Statuskode: {{statusCode}} (<3>{{statusText}}</3>)", "genericServerErrorBody": "Respons frå server:<1>{{errorBodyText}}</1>", - "contactHelp": "Kontakt hjelp via link i footer om problemet vedvarer." + "contactHelp": "Kontakt hjelp via link i footer om problemet vedvarer.", + "unknown": "Ein uventa feil oppstod. Om problemet varer ved, kontakt brukarstøtte." } } diff --git a/frontend/public/locales/nn/invite.json b/frontend/public/locales/nn/invite.json index abb2afaf0747f645944c94525cc49b28015e87bf..155b826bcf3b540b0a5bfb81ddfeb0d7c878bc6f 100644 --- a/frontend/public/locales/nn/invite.json +++ b/frontend/public/locales/nn/invite.json @@ -2,5 +2,9 @@ "description": "Ver venleg og vel korleis du vil logge inn for å fullføre registreringa. Den anbefalte metoden er å logge inn gjennom Feide eller ID-porten. Dersom det ikkje er mogeleg kan du fylle ut registreringskjemaet manuelt med passnummer", "header": "Gjestetjenesten", "login": "Logg inn med FEIDE", - "manual": "Registrer manuelt" + "manual": "Registrer manuelt", + "errors": { + "invalidToken": "Denne invitasjonslenka er ugyldig.", + "expiredToken": "Denne invitasjonslenka har utløpe. Kontakt verten din for å få tilsendt ei ny." + } } diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index e273442a88500c9057310d7a663a4b068b2be86d..1276166a6855b29848e62951470931c8caae4798 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -5,24 +5,24 @@ import { styled } from '@mui/system' import { CssBaseline } from '@mui/material' import fetchIntercept from 'fetch-intercept' +import { registerLocale } from 'i18n-iso-countries' +import i18n_iso_countries_en from 'i18n-iso-countries/langs/en.json' +import i18n_iso_countries_nb from 'i18n-iso-countries/langs/nb.json' +import i18n_iso_countries_nn from 'i18n-iso-countries/langs/nn.json' + import { useUserContext } from 'contexts' import { getCookie, deleteCookie } from 'utils' +import GuestRegister from 'routes/guest/register' import Sponsor from 'routes/sponsor' import Register from 'routes/sponsor/register' import FrontPage from 'routes/frontpage' import Invite from 'routes/invite' -import InviteLink from 'routes/invitelink' -import LogoutInviteSession from 'routes/invitelink/logout' +import LogoutInviteSession from 'routes/invite/logout' import Footer from 'routes/components/footer' import Header from 'routes/components/header' import NotFound from 'routes/components/notFound' import ProtectedRoute from 'components/protectedRoute' -import { registerLocale } from 'i18n-iso-countries' -import i18n_iso_countries_en from 'i18n-iso-countries/langs/en.json' -import i18n_iso_countries_nb from 'i18n-iso-countries/langs/nb.json' -import i18n_iso_countries_nn from 'i18n-iso-countries/langs/nn.json' -import GuestRegister from './guest/register' const AppWrapper = styled('div')({ display: 'flex', @@ -77,7 +77,6 @@ export default function App() { <ProtectedRoute path="/register"> <Register /> </ProtectedRoute> - <Route path="/invitelink/" component={InviteLink} /> <Route path="/invite/logout" component={LogoutInviteSession} /> <Route path="/invite/" component={Invite} /> <Route path="/guestregister" component={GuestRegister} /> diff --git a/frontend/src/routes/invite/index.tsx b/frontend/src/routes/invite/index.tsx index ba43e2d7c2a31266e2f209d6a838d44099b22f48..14acbc64312086b838447bf554ba76f48e48329e 100644 --- a/frontend/src/routes/invite/index.tsx +++ b/frontend/src/routes/invite/index.tsx @@ -1,25 +1,27 @@ +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Page from 'components/page' +import Loading from 'components/loading' import { styled } from '@mui/material/styles' +import { useUserContext } from 'contexts' import { HrefButton } from 'components/button' import { HrefLineButton } from 'components/button/linebutton' +import { submitJsonOpts } from 'utils' const FlexDiv = styled('div')(() => ({ display: 'flex', gap: '0.5rem', })) -function Invite() { +function ChooseRegistrationMethod() { const { t } = useTranslation(['invite']) return ( <Page> <h1>{t('header')}</h1> - <p> - {t('description')} - </p> + <p>{t('description')}</p> <FlexDiv> <HrefButton to="/oidc/authenticate/">{t('login')}</HrefButton> <HrefLineButton to="/guestregister/">{t('manual')}</HrefLineButton> @@ -28,4 +30,125 @@ function Invite() { ) } +interface ShowFeedbackProps { + title: string + description: string +} + +function ShowFeedback(props: ShowFeedbackProps) { + const { title, description } = props + + return ( + <Page> + <h1>{title}</h1> + <p>{description}</p> + </Page> + ) +} + +function Invite() { + const { t } = useTranslation(['invite']) + const { user, fetchUserInfo } = useUserContext() + const [inviteToken, setInviteToken] = useState('') + const [tokenChecked, setTokenChecked] = useState(false) + const [isCheckingToken, setIsCheckingToken] = useState(false) + const [tokenOk, setTokenOk] = useState(false) + const [checkError, setCheckError] = useState('') + + async function checkToken(token: string) { + try { + const response = await fetch( + '/api/ui/v1/invitecheck/', + submitJsonOpts('POST', { invite_token: token }) + ) + if (response.status === 200) { + setTokenOk(true) + return + } + const data = await response.json() + + if ('code' in data) { + setCheckError(data.code) + } else { + setCheckError('unknown') + } + } catch (error) { + console.error(error) + setCheckError('unknown') + } finally { + setTokenChecked(true) + setIsCheckingToken(false) + } + } + + console.log({ inviteToken, isCheckingToken, tokenChecked, tokenOk, user }) + + useEffect(() => { + setIsCheckingToken(true) + // This may seem unecessary, but race conditions have been + // observed where the userinfo endpoint is called too fast + // and no invite_id is found in the server-side session + setTimeout(fetchUserInfo, 100) + }, [setTokenOk]) + + if (user.auth) { + return <ChooseRegistrationMethod /> + } + + if (isCheckingToken || (tokenOk && !user.auth)) { + return <Loading /> + } + + if (inviteToken !== '' && !tokenChecked) { + checkToken(inviteToken) + return <Loading /> + } + + if (checkError !== '') { + if ( + checkError === 'missing_invite_token' || + checkError === 'invalid_invite_token' + ) { + // Missing or invalid token + return ( + <ShowFeedback + title={t('common:error.error')} + description={t('invite:errors.invalidToken')} + /> + ) + } + if (checkError === 'expired_invite_token') { + // Expired token + return ( + <ShowFeedback + title={t('common:error.error')} + description={t('invite:errors.expiredToken')} + /> + ) + } + // Unknown error + return ( + <ShowFeedback + title={t('common:error.error')} + description={t('common:error.unknown')} + /> + ) + } + + const providedToken = window.location.hash.slice(1).trim() + + if (!inviteToken && providedToken) { + setInviteToken(providedToken) + return <Loading /> + } + + // We'll end up here if no token was provided in the URL + return ( + <ShowFeedback + title={t('common:error.error')} + description={t('invite:errors.invalidToken')} + /> + ) +} + export default Invite diff --git a/frontend/src/routes/invitelink/logout.tsx b/frontend/src/routes/invite/logout.tsx similarity index 100% rename from frontend/src/routes/invitelink/logout.tsx rename to frontend/src/routes/invite/logout.tsx diff --git a/frontend/src/routes/invitelink/index.tsx b/frontend/src/routes/invitelink/index.tsx deleted file mode 100644 index fd8d63f056582d59de1a1774688a6d13506e8ec2..0000000000000000000000000000000000000000 --- a/frontend/src/routes/invitelink/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { useEffect } from 'react' -import { Redirect } from 'react-router-dom' -import { submitJsonOpts, setCookie } from 'utils' - -function InviteLink() { - // Fetch backend endpoint to preserve invite_id in backend session then redirect - // to generic invite page with info about feide login or manual with passport. - - const id = window.location.hash.slice(1) - - useEffect(() => { - fetch('/api/ui/v1/invitecheck/', submitJsonOpts('POST', { uuid: id })) - }, []) - setCookie('redirect', '/guestregister') - return <Redirect to="/invite" /> -} - -export default InviteLink diff --git a/gregui/api/views/invitation.py b/gregui/api/views/invitation.py index a979d534b95357fe0a02a5e5ee7efb2275dda1a7..34b6d4fa79bc078e488b1f6e5b70f176761b6da6 100644 --- a/gregui/api/views/invitation.py +++ b/gregui/api/views/invitation.py @@ -118,15 +118,33 @@ class CheckInvitationView(APIView): Uses post to prevent invite id from showing up in various logs. """ - invite_id = request.data.get("uuid") + invite_id = request.data.get("invite_token") if not invite_id: - return Response(status=status.HTTP_403_FORBIDDEN) + return Response( + status=status.HTTP_400_BAD_REQUEST, + data={ + "code": "missing_invite_token", + "message": "An invite token is required", + }, + ) try: invite_link = InvitationLink.objects.get(uuid=invite_id) except (InvitationLink.DoesNotExist, exceptions.ValidationError): - return Response(status=status.HTTP_403_FORBIDDEN) + return Response( + status=status.HTTP_403_FORBIDDEN, + data={ + "code": "invalid_invite_token", + "message": "Invite token is invalid", + }, + ) if invite_link.expire <= timezone.now(): - return Response(status=status.HTTP_403_FORBIDDEN) + return Response( + status=status.HTTP_403_FORBIDDEN, + data={ + "code": "expired_invite_token", + "message": "Invite token has expired", + }, + ) request.session["invite_id"] = invite_id return Response(status=status.HTTP_200_OK) diff --git a/gregui/api/views/userinfo.py b/gregui/api/views/userinfo.py index bba967c519e0c611d7018bd651a47c35bd984d96..f376fb0ad360d08257e4c52472ce80f06b19bd2d 100644 --- a/gregui/api/views/userinfo.py +++ b/gregui/api/views/userinfo.py @@ -21,8 +21,8 @@ class UserInfoView(APIView): Quick draft, we might want to expand this later. """ - authentication_classes: Sequence[Type[BaseAuthentication]] = [SessionAuthentication] - permission_classes: Sequence[Type[BasePermission]] = [AllowAny] + authentication_classes = [SessionAuthentication] + permission_classes = [AllowAny] def get(self, request, format=None): """ @@ -32,6 +32,7 @@ class UserInfoView(APIView): invitation id. Pure django users, and anonymous users are denied access. """ user = request.user + invite_id = request.session.get("invite_id") auth_type = "invite" @@ -40,6 +41,8 @@ class UserInfoView(APIView): person = None sponsor = None + user_profile = None + content = { "feide_id": None, "sponsor_id": None, @@ -48,34 +51,57 @@ class UserInfoView(APIView): "auth_type": auth_type, } - # Fetch sponsor and/or person object from profile of authenticated user if user.is_authenticated: try: - user_profile = GregUserProfile.objects.get(user=user) - sponsor = user_profile.sponsor - person = user_profile.person - content.update( - { - "feide_id": user_profile.userid_feide, - } - ) + user_profile = GregUserProfile.objects.select_related( + "person", "sponsor" + ).get(user=user) except GregUserProfile.DoesNotExist: - return Response(status=HTTP_403_FORBIDDEN) + return Response( + status=HTTP_403_FORBIDDEN, + data={ + "code": "no_user_profile", + "message": "Authenticated, but missing user profile", + }, + ) + # Fetch sponsor and/or person object from profile of authenticated user + if user_profile: + sponsor = user_profile.sponsor + person = user_profile.person + content["feide_id"] = user_profile.userid_feide # Or fetch person info for invited guest elif invite_id: - link = InvitationLink.objects.get(uuid=invite_id) + link = InvitationLink.objects.select_related( + "invitation__role__person", + ).get(uuid=invite_id) person = link.invitation.role.person - # Otherwise, deny access else: return Response(status=HTTP_403_FORBIDDEN) - # Add sponsor fields if sponsor object present if sponsor: - content.update({"sponsor_id": user_profile.sponsor.id}) - # Add person fields if person object present + content["sponsor_id"] = sponsor.id + if person: + person_roles = [ + { + "id": role.id, + "ou_nb": role.orgunit.name_nb, + "ou_en": role.orgunit.name_en, + "name_nb": role.type.name_nb, + "name_en": role.type.name_en, + "start_date": role.start_date, + "end_date": role.end_date, + "sponsor": { + "first_name": role.sponsor.first_name, + "last_name": role.sponsor.last_name, + }, + } + for role in person.roles.all().select_related( + "orgunit", "type", "sponsor" + ) + ] content.update( { "person_id": person.id, @@ -86,22 +112,7 @@ class UserInfoView(APIView): and person.private_mobile.value, "fnr": person.fnr and "".join((person.fnr.value[:-5], "*****")), "passport": person.passport and person.passport.value, - "roles": [ - { - "id": role.id, - "ou_nb": role.orgunit.name_nb, - "ou_en": role.orgunit.name_en, - "name_nb": role.type.name_nb, - "name_en": role.type.name_en, - "start_date": role.start_date, - "end_date": role.end_date, - "sponsor": { - "first_name": role.sponsor.first_name, - "last_name": role.sponsor.last_name, - }, - } - for role in person.roles.all() - ], + "roles": person_roles, } ) return Response(content) diff --git a/gregui/tests/api/views/test_invitation.py b/gregui/tests/api/views/test_invitation.py index 8a10b3727b196398e143cf0f25ad6e4165afe818..0fb8e4564adfae9e04d43e30b8b8beed83b8dfd0 100644 --- a/gregui/tests/api/views/test_invitation.py +++ b/gregui/tests/api/views/test_invitation.py @@ -12,15 +12,21 @@ from greg.models import Consent, InvitationLink, Person, Identity @pytest.mark.django_db def test_post_invite(client): """Forbid access with bad invitation link uuid""" - response = client.post(reverse("gregui-v1:invite-verify"), data={"uuid": "baduuid"}) + response = client.post( + reverse("gregui-v1:invite-verify"), data={"invite_token": "baduuid"} + ) assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.json() == { + "code": "invalid_invite_token", + "message": "Invite token is invalid", + } @pytest.mark.django_db def test_post_invite_ok(client, invitation_link): """Access okay with valid invitation link""" response = client.post( - reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid} + reverse("gregui-v1:invite-verify"), data={"invite_token": invitation_link.uuid} ) assert response.status_code == status.HTTP_200_OK @@ -29,23 +35,34 @@ def test_post_invite_ok(client, invitation_link): def test_post_invite_expired(client, invitation_link_expired): """Forbid access with expired invite link""" response = client.post( - reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link_expired.uuid} + reverse("gregui-v1:invite-verify"), + data={"invite_token": invitation_link_expired.uuid}, ) assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.json() == { + "code": "expired_invite_token", + "message": "Invite token has expired", + } @pytest.mark.django_db def test_post_missing_invite_id(client): """Forbid access if no id provided.""" response = client.post(reverse("gregui-v1:invite-verify")) - assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.json() == { + "code": "missing_invite_token", + "message": "An invite token is required", + } @pytest.mark.django_db def test_get_invited_info_no_session(client, invitation_link): """Forbid access if invite expired after session was created.""" # get a session - client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) + client.post( + reverse("gregui-v1:invite-verify"), data={"invite_token": invitation_link.uuid} + ) # expire the invitation link invitation_link.expire = timezone.now() - datetime.timedelta(days=1) invitation_link.save() @@ -60,7 +77,9 @@ def test_get_invited_info_session_okay( ): person, invitation_link = invited_person # get a session - client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) + client.post( + reverse("gregui-v1:invite-verify"), data={"invite_token": invitation_link.uuid} + ) # Get info response = client.get(reverse("gregui-v1:invited-info")) assert response.status_code == status.HTTP_200_OK @@ -95,7 +114,9 @@ def test_get_invited_info_expired_link( client, invitation_link, invitation_expired_date ): # Get a session while link is valid - client.get(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) + client.get( + reverse("gregui-v1:invite-verify"), data={"invite_token": invitation_link.uuid} + ) # Set expire link to expire long ago invlink = InvitationLink.objects.get(uuid=invitation_link.uuid) invlink.expire = invitation_expired_date @@ -110,7 +131,9 @@ def test_get_invited_info_expired_link( def test_invited_guest_can_post_information(client: APIClient, invited_person): person, invitation_link = invited_person # get a session - client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) + client.post( + reverse("gregui-v1:invite-verify"), data={"invite_token": invitation_link.uuid} + ) person = invitation_link.invitation.role.person assert person.private_mobile is None @@ -138,7 +161,9 @@ def test_post_invited_info_expired_session( client, invitation_link, invitation_expired_date ): # get a session - client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) + client.post( + reverse("gregui-v1:invite-verify"), data={"invite_token": invitation_link.uuid} + ) # Set expire link to expire long ago invlink = InvitationLink.objects.get(uuid=invitation_link.uuid) invlink.expire = invitation_expired_date @@ -152,7 +177,9 @@ def test_post_invited_info_expired_session( @pytest.mark.django_db def test_post_invited_info_deleted_inv_link(client, invitation_link): # get a session - client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) + client.post( + reverse("gregui-v1:invite-verify"), data={"invite_token": invitation_link.uuid} + ) # Delete link invlink = InvitationLink.objects.get(uuid=invitation_link.uuid) invlink.delete() @@ -284,7 +311,9 @@ def test_name_update_not_allowed_if_feide_identity_is_present( ) invitation = create_invitation(role) invitation_link = create_invitation_link(invitation, invitation_valid_date) - client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) + client.post( + reverse("gregui-v1:invite-verify"), data={"invite_token": invitation_link.uuid} + ) person = invitation_link.invitation.role.person data = { @@ -330,7 +359,9 @@ def test_name_update_allowed_if_feide_identity_is_not_present( invitation_link = create_invitation_link( create_invitation(role), invitation_valid_date ) - client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) + client.post( + reverse("gregui-v1:invite-verify"), data={"invite_token": invitation_link.uuid} + ) person = invitation_link.invitation.role.person data = { diff --git a/gregui/tests/api/views/test_userinfo.py b/gregui/tests/api/views/test_userinfo.py index cf1f331d48c6ff3ca1fe2d2ec47f4db756ac7c68..df7d3cb44888f333ac0939a4509c0ec88583fb0d 100644 --- a/gregui/tests/api/views/test_userinfo.py +++ b/gregui/tests/api/views/test_userinfo.py @@ -6,7 +6,7 @@ from rest_framework import status @pytest.mark.django_db def test_userinfo_anon_get(client): """Anonymous people should be forbidden.""" - response = client.get(reverse("api-userinfo")) + response = client.get(reverse("gregui-v1:userinfo")) assert response.status_code == status.HTTP_403_FORBIDDEN @@ -16,7 +16,7 @@ def test_userinfo_invited_get(client, invitation_link): session = client.session session["invite_id"] = str(invitation_link.uuid) session.save() - response = client.get(reverse("api-userinfo")) + response = client.get(reverse("gregui-v1:userinfo")) assert response.status_code == status.HTTP_200_OK assert response.json() == { "auth_type": "invite", @@ -48,7 +48,7 @@ def test_userinfo_invited_get(client, invitation_link): def test_userinfo_user_no_profile(client, log_in, user_no_profile): """Pure django users should be forbidden""" log_in(user_no_profile) - response = client.get(reverse("api-userinfo")) + response = client.get(reverse("gregui-v1:userinfo")) assert response.status_code == status.HTTP_403_FORBIDDEN @@ -57,7 +57,7 @@ def test_userinfo_sponsor_get(client, log_in, user_sponsor): """Sponsors should get info about themselves""" log_in(user_sponsor) - response = client.get(reverse("api-userinfo")) + response = client.get(reverse("gregui-v1:userinfo")) assert response.status_code == status.HTTP_200_OK assert response.json() == { "auth_type": "oidc", @@ -72,7 +72,7 @@ def test_userinfo_sponsor_get(client, log_in, user_sponsor): def test_userinfo_guest_get(client, log_in, user_person): """Logged in guests should get info about themself""" log_in(user_person) - response = client.get(reverse("api-userinfo")) + response = client.get(reverse("gregui-v1:userinfo")) assert response.status_code == status.HTTP_200_OK assert response.json() == { "auth_type": "oidc",