Skip to content

Commit

Permalink
Allow userId change in dev mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ineiti committed Oct 6, 2023
1 parent 98f3945 commit 7bd1203
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 20 deletions.
4 changes: 2 additions & 2 deletions scripts/local_forms.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ sleep 8
curl -k "$FRONTEND_URL/api/evoting/forms/$FORMID" -X PUT -b cookies.txt -H 'Content-Type: application/json' --data-raw '{"Action":"open"}' >/dev/null
echo "Form with ID $FORMID has been set up"

echo "Adding $SCIPER_ADMIN to voters"
echo "Adding $REACT_APP_SCIPER_ADMIN to voters"
tmpfile=$(mktemp)
echo -n "$SCIPER_ADMIN" >"$tmpfile"
echo -n "$REACT_APP_SCIPER_ADMIN" >"$tmpfile"
(cd web/backend && npx ts-node src/cli.ts addVoters --election-id $FORMID --scipers-file "$tmpfile")
echo "Restarting backend to take into account voters"
"$SCRIPT_DIR/run_local.sh" backend
Expand Down
2 changes: 1 addition & 1 deletion scripts/local_login.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
. "$SCRIPT_DIR/local_vars.sh"

if ! [[ -f cookies.txt ]]; then
curl -k "$FRONTEND_URL/api/get_dev_login" -X GET -c cookies.txt -o /dev/null -s
curl -k "$FRONTEND_URL/api/get_dev_login/$REACT_APP_SCIPER_ADMIN" -X GET -c cookies.txt -o /dev/null -s
fi
4 changes: 2 additions & 2 deletions scripts/local_vars.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export SCIPER_ADMIN=100100
export DATABASE_USERNAME=dvoting
export DATABASE_PASSWORD=postgres
export FRONTEND_URL="http://localhost:3000"
Expand All @@ -17,8 +16,9 @@ export DB_PATH="$(pwd)/nodes/llmdb"
#export PROXY_LOG=info
# For the Dela node itself (info, debug):
#export LLVL=debug
# Logging in without Gaspar and SCIPER 100100
# Logging in without Gaspar and REACT_APP_SCIPER_ADMIN
export REACT_APP_DEV_LOGIN="true"
export REACT_APP_SCIPER_ADMIN=100100
# uncomment this to enable TLS to test gaspar
#export HTTPS=true
# Create random voter-IDs to allow easier testing
Expand Down
4 changes: 2 additions & 2 deletions scripts/run_local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ function init_db() {
-e POSTGRES_PASSWORD=$DATABASE_PASSWORD -e POSTGRES_USER=$DATABASE_USERNAME \
--name postgres_dvoting -p 5432:5432 postgres:15 >/dev/null

echo "Adding $SCIPER_ADMIN to admin"
(cd web/backend && npx ts-node src/cli.ts addAdmin --sciper $SCIPER_ADMIN | grep -v Executing)
echo "Adding $REACT_APP_SCIPER_ADMIN to admin"
(cd web/backend && npx ts-node src/cli.ts addAdmin --sciper $REACT_APP_SCIPER_ADMIN | grep -v Executing)
}

function kill_backend() {
Expand Down
14 changes: 7 additions & 7 deletions web/backend/src/controllers/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@ import { getUserPermissions, readSCIPER, setMapAuthorization } from '../authMana

export const authenticationRouter = express.Router();

authenticationRouter.get('/get_dev_login', (req, res) => {
authenticationRouter.get('/get_dev_login/:userId', (req, res) => {
if (process.env.REACT_APP_DEV_LOGIN !== 'true') {
const err = `/get_dev_login can only be called with REACT_APP_DEV_LOGIN===true: ${process.env.REACT_APP_DEV_LOGIN}`;
console.error(err);
res.status(500).send(err);
return;
}
if (process.env.SCIPER_ADMIN === undefined) {
const err = 'Please set SCIPER_ADMIN for /get/dev/login endpoint';
if (req.params.userId === undefined) {
const err = 'no userId given';
console.error(err);
res.status(500).send(err);
return;
}
try {
req.session.userId = readSCIPER(process.env.SCIPER_ADMIN);
req.session.lastName = 'develo';
req.session.firstName = 'pment';
req.session.userId = readSCIPER(req.params.userId);
req.session.lastName = 'sciper-#';
req.session.firstName = req.params.userId;
} catch (e) {
const err = `Invalid SCIPER_ADMIN: ${e}`;
const err = `Invalid userId: ${e}`;
console.error(err);
res.status(500).send(err);
return;
Expand Down
1 change: 1 addition & 0 deletions web/frontend/src/language/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"notLoggedInActionText3": " um diese Aktionen durchzuführen.",
"loginCallback": "Wir fahren mit der Authentifizierung fort. Sie sollten weitergeleitet werden...",
"logout": "Abmeldung",
"changeId": "UserId ändern",
"namePlaceHolder": "Geben Sie den Namen ein",
"addCandidate": "Einen Kandidaten hinzufügen",
"addUser": "Benutzer hinzufügen",
Expand Down
1 change: 1 addition & 0 deletions web/frontend/src/language/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"notLoggedInActionText3": " to perform these actions.",
"loginCallback": "We are proceeding with the authentication. You should be redirected...",
"logout": "Logout",
"changeId": "Change UserId",
"namePlaceHolder": "Enter the name",
"addCandidate": "Add a candidate",
"addUser": "Add user",
Expand Down
1 change: 1 addition & 0 deletions web/frontend/src/language/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"notLoggedInActionText3": " pour effectuer ces actions.",
"loginCallback": "Nous procédons à l'authentification. Vous devriez être redirigé...",
"logout": "Déconnexion",
"changeId": "Changer le userId",
"namePlaceHolder": "Entrer le nom",
"addCandidate": "Ajouter un candidat",
"addUser": "Ajouter un utilisateur",
Expand Down
25 changes: 22 additions & 3 deletions web/frontend/src/layout/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ROUTE_FORM_INDEX,
ROUTE_HOME,
} from '../Routes';
import ChangeIdModal from './components/ChangeIdModal';

import WarningModal from './components/WarningModal';
import { AuthContext, FlashContext, FlashLevel } from '..';
Expand Down Expand Up @@ -153,7 +154,7 @@ const MobileMenu = ({ authCtx, handleLogout, fctx, t }) => (
</Popover>
);

const RightSideNavBar = ({ authCtx, handleLogout, fctx, t }) => (
const RightSideNavBar = ({ authCtx, handleLogout, handleChangeId, fctx, t }) => (
<div className="absolute hidden inset-y-0 right-0 flex items-center pr-2 md:static md:inset-auto md:flex md:ml-6 md:pr-0">
{authCtx.isLogged && authCtx.isAllowed(SUBJECT_ELECTION, ACTION_CREATE) && (
<NavLink title={t('navBarCreateForm')} to={ROUTE_FORM_CREATE}>
Expand All @@ -164,7 +165,13 @@ const RightSideNavBar = ({ authCtx, handleLogout, fctx, t }) => (
</NavLink>
)}
<LanguageSelector />
<Profile authCtx={authCtx} handleLogout={handleLogout} handleLogin={handleLogin} fctx={fctx} />
<Profile
authCtx={authCtx}
handleLogout={handleLogout}
handleLogin={handleLogin}
handleChangeId={handleChangeId}
fctx={fctx}
/>
</div>
);

Expand Down Expand Up @@ -206,6 +213,7 @@ const NavBar: FC = () => {

const fctx = useContext(FlashContext);
const [isShown, setIsShown] = useState(false);
const [changeIdShown, setChangeIdShown] = useState(false);

const logout = async () => {
const opts = { method: 'POST' };
Expand All @@ -228,19 +236,30 @@ const NavBar: FC = () => {
setIsShown(true);
};

const handleChangeId = () => {
setChangeIdShown(true);
};

return (
<nav className="w-full border-b">
<div className="max-w-7xl mx-auto px-2 md:px-6 lg:px-8">
<div className="relative flex items-center justify-between h-16">
<MobileMenu authCtx={authCtx} handleLogout={handleLogout} fctx={fctx} t={t} />
<LeftSideNavBar authCtx={authCtx} t={t} />
<RightSideNavBar authCtx={authCtx} handleLogout={handleLogout} fctx={fctx} t={t} />
<RightSideNavBar
authCtx={authCtx}
handleLogout={handleLogout}
handleChangeId={handleChangeId}
fctx={fctx}
t={t}
/>
<WarningModal
isShown={isShown}
setIsShown={setIsShown}
action={async () => logout()}
message={t('logoutWarning')}
/>
<ChangeIdModal isShown={changeIdShown} setIsShown={setChangeIdShown} />
</div>
</div>
</nav>
Expand Down
89 changes: 89 additions & 0 deletions web/frontend/src/layout/components/ChangeIdModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { FC, useContext, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Dialog } from '@headlessui/react';
import { useTranslation } from 'react-i18next';
import { ENDPOINT_LOGOUT } from '../../components/utils/Endpoints';
import { AuthContext, FlashContext, FlashLevel } from '../../index';
import handleLogin from '../../pages/session/HandleLogin';

type ChangeIdModalProps = {
isShown: boolean;
setIsShown: (isShown: boolean) => void;
};

const logout = async (t, authCtx, fctx, navigate) => {
const opts = { method: 'POST' };

const res = await fetch(ENDPOINT_LOGOUT, opts);
if (res.status !== 200) {
fctx.addMessage(t('logOutError', { error: res.statusText }), FlashLevel.Error);
} else {
fctx.addMessage(t('logOutSuccessful'), FlashLevel.Info);
}
// TODO: should be a setAuth function passed to AuthContext rather than
// changing the state directly
authCtx.isLogged = false;
authCtx.firstName = undefined;
authCtx.lastName = undefined;
navigate('/');
};

const ChangeIdModal: FC<ChangeIdModalProps> = ({ isShown, setIsShown }) => {
const { t } = useTranslation();
const [newId, setNewId] = useState('');
const authCtx = useContext(AuthContext);

const navigate = useNavigate();

const fctx = useContext(FlashContext);

async function changeId() {
logout(t, authCtx, fctx, navigate).then(() => {
handleLogin(fctx, newId).catch(console.error);
});
}

if (isShown) {
return (
<Dialog open={isShown} onClose={() => {}}>
<Dialog.Overlay className="fixed inset-0 bg-black opacity-30" />
<div className="fixed inset-0 flex items-center justify-center">
<div className="bg-white content-center rounded-lg shadow-lg p-3 w-80">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900">
{t('changeIdTitle')}
</Dialog.Title>
<Dialog.Description className="mt-2 mx-auto text-sm text-center text-gray-500">
{t('changeIdDialog')}
</Dialog.Description>
<div className="mt-4 sm:mt-6 sm:grid sm:grid-cols-2 sm:gap-3 sm:grid-flow-row-dense">
<input
onChange={(e) => setNewId(e.target.value)}
type="number"
placeholder={t('changeIdPlaceholder')}
/>
<button
type="button"
className="inline-flex justify-center px-4 py-2 text-sm font-medium text-white bg-indigo-500 border border-transparent rounded-md hover:bg-gray-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-indigo-500"
onClick={() => setIsShown(false)}>
{t('cancel')}
</button>
<button
type="button"
className="inline-flex justify-center px-4 py-2 text-sm font-medium text-white bg-red-600 border border-transparent rounded-md hover:bg-red-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-red-500"
onClick={() => {
setIsShown(false);
changeId();
}}>
{t('changeIdContinue')}
</button>
</div>
</div>
</div>
</Dialog>
);
} else {
return <></>;
}
};

export default ChangeIdModal;
18 changes: 17 additions & 1 deletion web/frontend/src/layout/components/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ type ProfileProps = {
authCtx: AuthState;
handleLogout: (e: any) => Promise<void>;
handleLogin: (arg0: FlashState) => Promise<void>;
handleChangeId: (e: any) => Promise<void>;
fctx: FlashState;
};

const Profile: FC<ProfileProps> = ({ authCtx, handleLogout, handleLogin, fctx }) => {
const Profile: FC<ProfileProps> = ({
authCtx,
handleLogout,
handleLogin,
handleChangeId,
fctx,
}) => {
const { t } = useTranslation();

return authCtx.isLogged ? (
Expand All @@ -38,6 +45,15 @@ const Profile: FC<ProfileProps> = ({ authCtx, handleLogout, handleLogin, fctx })
Logged as {authCtx.firstName} {authCtx.lastName}
</p>
</Menu.Item>
{process.env.REACT_APP_DEV_LOGIN === 'true' && (
<Menu.Item>
<div
onClick={handleChangeId}
className={'cursor-pointer block px-4 py-2 text-sm text-gray-700'}>
{t('changeId')}
</div>
</Menu.Item>
)}
<Menu.Item>
<div
onClick={handleLogout}
Expand Down
5 changes: 3 additions & 2 deletions web/frontend/src/pages/session/HandleLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { FlashLevel, FlashState } from 'index';
// The backend will provide the client the URL to make a Tequila authentication.
// We therefore redirect to this address.
// If REACT_APP_DEV_LOGIN === "true", we allow an automatic login with SCIPER 100100.
const handleLogin = async (fctx: FlashState) => {
const handleLogin = async (fctx: FlashState, dev_id = process.env.REACT_APP_SCIPER_ADMIN) => {
console.log(`dev_id is: ${dev_id}`);
try {
let res;
if (process.env.REACT_APP_DEV_LOGIN === 'true') {
await fetch(ENDPOINT_DEV_LOGIN);
await fetch(ENDPOINT_DEV_LOGIN + `/${dev_id}`);
window.location.reload();
return;
} else {
Expand Down

0 comments on commit 7bd1203

Please sign in to comment.