diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index 40b95593383c15ecec08a67feddc1c8e1d83bc7e..3ad2e49a232ab7d75fb9b916fa9cedce8a96693e 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -64,7 +64,7 @@ export default function App() { <ProtectedRoute path="/register"> <Register /> </ProtectedRoute> - <Route path="/invite/:id" component={InviteLink} /> + <Route path="/invitelink/" component={InviteLink} /> <Route path="/invite/" component={Invite} /> <Route path="/guestregister/" component={GuestRegister} /> <Route> diff --git a/frontend/src/routes/invitelink/index.tsx b/frontend/src/routes/invitelink/index.tsx index 32dc16d0a05eb0debf3469e1224a2c013dffdad0..eb839a2480a1f5172c0d3ca115f38bc3881b2ced 100644 --- a/frontend/src/routes/invitelink/index.tsx +++ b/frontend/src/routes/invitelink/index.tsx @@ -1,16 +1,15 @@ import { useEffect } from 'react' -import { Redirect, useParams } from 'react-router-dom' - -type TParams = { id: string } +import { Redirect } from 'react-router-dom' +import { submitJsonOpts } 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 } = useParams<TParams>() + const id = window.location.hash.slice(1) useEffect(() => { - fetch(`/api/ui/v1/invited/${id}`) + fetch('/api/ui/v1/invitecheck/', submitJsonOpts('POST', { uuid: id })) }, []) return <Redirect to="/invite" /> } diff --git a/gregui/api/urls.py b/gregui/api/urls.py index 46e1cf0fbe918a4bf033cc9b52d133fb0bea1b79..49a4902f5730cf416cf206b0bfc6bbdaf2a820aa 100644 --- a/gregui/api/urls.py +++ b/gregui/api/urls.py @@ -20,7 +20,7 @@ urlpatterns += [ re_path(r"roletypes/$", RoleTypeViewSet.as_view(), name="role-types"), re_path(r"units/$", UnitsViewSet.as_view(), name="units"), path("invited/", InvitedGuestView.as_view(), name="invited-info"), - path("invited/<uuid>", CheckInvitationView.as_view(), name="invite-verify"), + path("invitecheck/", CheckInvitationView.as_view(), name="invite-verify"), path("invite/", InvitationView.as_view(), name="invitation"), path("person/<int:id>", PersonView.as_view(), name="person-get"), path( diff --git a/gregui/api/views/invitation.py b/gregui/api/views/invitation.py index 5331d43fb6f13da8a8f30ed84da72ce8fc7673c6..5a293b3f6e5313f5cc81ec8f458586539d575bc1 100644 --- a/gregui/api/views/invitation.py +++ b/gregui/api/views/invitation.py @@ -97,7 +97,7 @@ class CheckInvitationView(APIView): authentication_classes = [] permission_classes = [AllowAny] - def get(self, request, *args, **kwargs): + def post(self, request, *args, **kwargs): """ Endpoint for verifying and setting invite_id in session. @@ -105,8 +105,12 @@ class CheckInvitationView(APIView): page you get to by following the invitation url to the frontend. This way a session is created keeping the invite id safe, until the user returns from feide login if they choose to use it. + + Uses post to prevent invite id from showing up in various logs. """ - invite_id = kwargs["uuid"] + invite_id = request.data.get("uuid") + if not invite_id: + return Response(status=status.HTTP_403_FORBIDDEN) try: invite_link = InvitationLink.objects.get(uuid=invite_id) except (InvitationLink.DoesNotExist, exceptions.ValidationError): diff --git a/gregui/tests/api/test_invitation.py b/gregui/tests/api/test_invitation.py index c93984183f5b58e67931a880a810fe5ac6234fe8..99b6adc0b8048978a388dbec518bc834f5f1841d 100644 --- a/gregui/tests/api/test_invitation.py +++ b/gregui/tests/api/test_invitation.py @@ -10,17 +10,15 @@ from greg.models import InvitationLink, Person, Identity @pytest.mark.django_db def test_get_invite(client): """Forbid access with bad invitation link uuid""" - response = client.get( - reverse("gregui-v1:invite-verify", kwargs={"uuid": "baduuid"}) - ) + response = client.post(reverse("gregui-v1:invite-verify"), data={"uuid": "baduuid"}) assert response.status_code == status.HTTP_403_FORBIDDEN @pytest.mark.django_db def test_get_invite_ok(client, invitation_link): """Access okay with valid invitation link""" - response = client.get( - reverse("gregui-v1:invite-verify", kwargs={"uuid": invitation_link.uuid}) + response = client.post( + reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid} ) assert response.status_code == status.HTTP_200_OK @@ -28,10 +26,8 @@ def test_get_invite_ok(client, invitation_link): @pytest.mark.django_db def test_get_invite_expired(client, invitation_link_expired): """Forbid access with expired invite link""" - response = client.get( - reverse( - "gregui-v1:invite-verify", kwargs={"uuid": invitation_link_expired.uuid} - ) + response = client.post( + reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link_expired.uuid} ) assert response.status_code == status.HTTP_403_FORBIDDEN @@ -47,9 +43,7 @@ def test_get_invited_info_session_okay( client, invitation_link, person_foo_data, sponsor_guy_data, role_type_foo, unit_foo ): # get a session - client.get( - reverse("gregui-v1:invite-verify", kwargs={"uuid": invitation_link.uuid}) - ) + client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) # Get info response = client.get(reverse("gregui-v1:invited-info")) assert response.status_code == status.HTTP_200_OK @@ -82,9 +76,7 @@ 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", kwargs={"uuid": invitation_link.uuid}) - ) + client.get(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) # Set expire link to expire long ago invlink = InvitationLink.objects.get(uuid=invitation_link.uuid) invlink.expire = invitation_expired_date @@ -100,9 +92,7 @@ def test_invited_guest_can_post_information( client: APIClient, invitation_link, person_foo_data ): # get a session - client.get( - reverse("gregui-v1:invite-verify", kwargs={"uuid": invitation_link.uuid}) - ) + client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) person = invitation_link.invitation.role.person assert person.private_mobile is None @@ -129,9 +119,7 @@ def test_post_invited_info_expired_session( client, invitation_link, invitation_expired_date ): # get a session - client.get( - reverse("gregui-v1:invite-verify", kwargs={"uuid": invitation_link.uuid}) - ) + client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) # Set expire link to expire long ago invlink = InvitationLink.objects.get(uuid=invitation_link.uuid) invlink.expire = invitation_expired_date @@ -145,9 +133,7 @@ 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.get( - reverse("gregui-v1:invite-verify", kwargs={"uuid": invitation_link.uuid}) - ) + client.post(reverse("gregui-v1:invite-verify"), data={"uuid": invitation_link.uuid}) # Delete link invlink = InvitationLink.objects.get(uuid=invitation_link.uuid) invlink.delete()