Skip to content

Commit

Permalink
fix: non-voter user should not be able to see voting screen
Browse files Browse the repository at this point in the history
  • Loading branch information
PascalinDe committed Feb 15, 2024
1 parent 424cdff commit d4fb1b3
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 14 deletions.
6 changes: 4 additions & 2 deletions web/frontend/src/language/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@
"operationFailure": "Der Vorgang ist fehlgeschlagen. Versuchen Sie, die Seite zu aktualisieren.",
"shuffleFail": "Die Zufallsmischung ist fehlgeschlagen.",
"voteImpossible": "Unmöglich abstimmen",
"notFoundVoteImpossible": "Zurück zur Formulartabelle",
"returnToFormTable": "Zurück zur Formulartabelle",
"voteImpossibleDescription": "Das Formular ist nicht mehr zur Abstimmung geöffnet.",
"yes": "Ja",
"no": "Nein",
Expand Down Expand Up @@ -289,6 +289,8 @@
"footerUnknown": "?",
"footerVersion": "version:",
"footerBuild": "build:",
"footerBuildTime": "in:"
"footerBuildTime": "in:",
"voteNotVoter": "Wählen nicht erlaubt.",
"voteNotVoterDescription": "Sie sind nicht wahlberechtigt in dieser Wahl. Falls Sie denken, dass ein Fehler vorliegt, wenden Sie sich bitte an die verantwortliche Stelle."
}
}
6 changes: 4 additions & 2 deletions web/frontend/src/language/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@
"operationFailure": "The operation failed. Try refreshing the page.",
"shuffleFail": "The shuffle operation failed.",
"voteImpossible": "Vote Impossible",
"notFoundVoteImpossible": "Go back to form table",
"returnToFormTable": "Go back to form table",
"voteImpossibleDescription": "The form is not open for voting anymore.",
"yes": "Yes",
"no": "No",
Expand Down Expand Up @@ -290,6 +290,8 @@
"footerUnknown": "?",
"footerVersion": "version:",
"footerBuild": "build:",
"footerBuildTime": "in:"
"footerBuildTime": "in:",
"voteNotVoter": "Voting not allowed.",
"voteNotVoterDescription": "You are not allowed to vote in this form. If you believe this is an error, please contact the responsible of the service."
}
}
6 changes: 4 additions & 2 deletions web/frontend/src/language/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@
"operationFailure": "L'opération a échoué. Essayez de rafraichir la page.",
"shuffleFail": "L'opération de mélange a échoué",
"voteImpossible": "Vote Impossible",
"notFoundVoteImpossible": "Retournez à l'onglet des sondages",
"returnToFormTable": "Retournez à l'onglet des sondages",
"voteImpossibleDescription": "Le sondage n'est plus ouvert au vote.",
"yes": "Oui",
"no": "Non",
Expand Down Expand Up @@ -289,6 +289,8 @@
"footerUnknown": "?",
"footerVersion": "version:",
"footerBuild": "build:",
"footerBuildTime": "en:"
"footerBuildTime": "en:",
"voteNotVoter": "Interdit de voter.",
"voteNotVoterDescription": "Vous n'avez pas le droit de voter dans cette élection. Si vous pensez qu'il s'agit d'une erreur, veuillez contacter le/la reponsable de service."
}
}
14 changes: 10 additions & 4 deletions web/frontend/src/pages/ballot/Show.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { FC, useState } from 'react';
import { FC, useContext, useState } from 'react';
import { AuthContext } from 'index';
import { isVoter } from './../../utils/auth';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import kyber from '@dedis/kyber';
Expand All @@ -17,7 +19,7 @@ import { useConfiguration } from 'components/utils/useConfiguration';
import { Status } from 'types/form';
import { ballotIsValid } from './components/ValidateAnswers';
import BallotDisplay from './components/BallotDisplay';
import FormClosed from './components/FormClosed';
import FormNotAvailable from './components/FormNotAvailable';
import Loading from 'pages/Loading';
import RedirectToModal from 'components/modal/RedirectToModal';
import { default as i18n } from 'i18next';
Expand All @@ -39,6 +41,7 @@ const Ballot: FC = () => {
const [castVoteLoading, setCastVoteLoading] = useState(false);

const navigate = useNavigate();
const { authorization, isLogged } = useContext(AuthContext);

const hexToBytes = (hex: string) => {
const bytes: number[] = [];
Expand Down Expand Up @@ -113,6 +116,8 @@ const Ballot: FC = () => {
event.currentTarget.disabled = true;
};

const userIsVoter = isVoter(formID, authorization, isLogged);

return (
<>
<RedirectToModal
Expand All @@ -127,7 +132,7 @@ const Ballot: FC = () => {
<Loading />
) : (
<>
{status === Status.Open && (
{status === Status.Open && userIsVoter && (
<div className="w-[60rem] font-sans px-4 pt-8 pb-4">
<div className="pb-2">
<h2 className="text-2xl font-bold leading-7 text-gray-900 sm:text-3xl sm:truncate">
Expand Down Expand Up @@ -165,7 +170,8 @@ const Ballot: FC = () => {
</div>
</div>
)}
{status !== Status.Open && <FormClosed />}
{!userIsVoter && <FormNotAvailable isVoter={false} />}
{status !== Status.Open && <FormNotAvailable isVoter={true} />}
</>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { ROUTE_FORM_INDEX } from 'Routes';

export default function FormClosed() {
export default function FormNotAvailable(props) {
const { t } = useTranslation();

return (
Expand All @@ -13,15 +13,17 @@ export default function FormClosed() {
<div className="sm:ml-6">
<div className=" sm:border-gray-200 sm:pl-6">
<h1 className="text-4xl font-extrabold text-gray-900 tracking-tight sm:text-5xl">
{t('voteImpossible')}
{props.isVoter ? t('voteImpossible') : t('voteNotVoter')}
</h1>
<p className="mt-1 text-base text-gray-500">{t('voteImpossibleDescription')}</p>
<p className="mt-1 text-base text-gray-500">
{props.isVoter ? t('voteImpossibleDescription') : t('voteNotVoterDescription')}
</p>
</div>
<div className="mt-10 flex space-x-3 sm:border-l sm:border-transparent sm:pl-6">
<Link
to={ROUTE_FORM_INDEX}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
{t('notFoundVoteImpossible')}
{t('returnToFormTable')}
</Link>
</div>
</div>
Expand Down
49 changes: 49 additions & 0 deletions web/frontend/tests/ballot.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { expect, test } from '@playwright/test';
import { default as i18n } from 'i18next';
import { assertHasFooter, assertHasNavBar, initI18n, logIn, setUp } from './shared';
import { FORMID } from './mocks/shared';
import { SCIPER_ADMIN, SCIPER_OTHER_USER, SCIPER_USER, mockPersonalInfo } from './mocks/api';
import { mockFormsFormID } from './mocks/evoting';

initI18n();

test.beforeEach(async ({ page }) => {
await mockFormsFormID(page, 1);
await logIn(page, SCIPER_ADMIN);
await setUp(page, `/ballot/show/${FORMID}`);
});

test('Assert navigation bar is present', async ({ page }) => {
await assertHasNavBar(page);
});

test('Assert footer is present', async ({ page }) => {
await assertHasFooter(page);
});

test('Assert ballot form is correctly handled for anonymous users, non-voter users and voter users', async ({
page,
}) => {
const castVoteButton = await page.getByRole('button', { name: i18n.t('castVote') });
await test.step('Assert anonymous is redirected to login page', async () => {
await mockPersonalInfo(page);
await page.reload({ waitUntil: 'networkidle' });
await expect(page).toHaveURL('/login');
});
await test.step('Assert non-voter gets page that they are not allowed to vote', async () => {
await logIn(page, SCIPER_OTHER_USER);
await page.goto(`/ballot/show/${FORMID}`, { waitUntil: 'networkidle' });
await expect(page).toHaveURL(`/ballot/show/${FORMID}`);
await expect(castVoteButton).toBeHidden();
await expect(page.getByText(i18n.t('voteNotVoter'))).toBeVisible();
await expect(page.getByText(i18n.t('voteNotVoterDescription'))).toBeVisible();
});
await test.step('Assert voter gets ballot', async () => {
await logIn(page, SCIPER_USER);
await page.goto(`/ballot/show/${FORMID}`, { waitUntil: 'networkidle' });
await expect(page).toHaveURL(`/ballot/show/${FORMID}`);
await expect(castVoteButton).toBeVisible();
await expect(page.getByText(i18n.t('vote'))).toBeVisible();
await expect(page.getByText(i18n.t('voteExplanation'))).toBeVisible();
});
});

0 comments on commit d4fb1b3

Please sign in to comment.