Skip to content

Commit

Permalink
Merge pull request #34 from c4dt/add_voters
Browse files Browse the repository at this point in the history
Add voters
  • Loading branch information
ineiti authored Oct 10, 2023
2 parents 4baccdf + 27a1c2e commit 275862a
Show file tree
Hide file tree
Showing 21 changed files with 503 additions and 72 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Latest changes in each category go to the top
## [Unreleased]

### Added
- dev_login can change userId when clicking on the user in the upper right
- admin can now add users as voters
- New debugging variables in [local_vars.sh](./scripts/local_vars.sh)
- Changelog - please use it

Expand Down
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
19 changes: 9 additions & 10 deletions web/backend/src/authManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,19 @@ export function isAuthorized(sciper: number | undefined, subject: string, action
}

export async function getUserPermissions(userID: number) {
let permissions: string[][] = [];
await authEnforcer.getFilteredPolicy(0, String(userID)).then((authRights) => {
permissions = authRights;
});
console.log(`[getUserPermissions] user has permissions: ${permissions}`);
return permissions;
return authEnforcer.getFilteredPolicy(0, String(userID));
}

export function assignUserPermissionToOwnElection(userID: string, ElectionID: string) {
authEnforcer.addPolicy(userID, ElectionID, PERMISSIONS.ACTIONS.OWN);
export async function addPolicy(userID: string, subject: string, permission: string) {
await authEnforcer.addPolicy(userID, subject, permission);
await authEnforcer.loadPolicy();
}
export async function assignUserPermissionToOwnElection(userID: string, ElectionID: string) {
return authEnforcer.addPolicy(userID, ElectionID, PERMISSIONS.ACTIONS.OWN);
}

export function revokeUserPermissionToOwnElection(userID: string, ElectionID: string) {
authEnforcer.removePolicy(userID, ElectionID, PERMISSIONS.ACTIONS.OWN);
export async function revokeUserPermissionToOwnElection(userID: string, ElectionID: string) {
return authEnforcer.removePolicy(userID, ElectionID, PERMISSIONS.ACTIONS.OWN);
}

// This function helps us convert the double list of the authorization
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.firstName = 'sciper-#';
req.session.lastName = 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
19 changes: 10 additions & 9 deletions web/backend/src/controllers/users.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import express from 'express';

import { isAuthorized, PERMISSIONS, readSCIPER } from '../authManager';
import { addPolicy, isAuthorized, PERMISSIONS } from '../authManager';

export const usersRouter = express.Router();

Expand All @@ -19,21 +19,22 @@ usersRouter.get('/user_rights', (req, res) => {
res.json(users);
});

// This call (only for admins) allow an admin to add a role to a voter.
// This call (only for admins) allows an admin to add a role to a voter.
usersRouter.post('/add_role', (req, res, next) => {
if (!isAuthorized(req.session.userId, PERMISSIONS.SUBJECTS.ROLES, PERMISSIONS.ACTIONS.ADD)) {
res.status(400).send('Unauthorized - only admins allowed');
return;
}

try {
readSCIPER(req.body.sciper);
} catch (e) {
res.status(400).send('Sciper length is incorrect');
return;
}
addPolicy(req.body.userId, req.body.subject, req.body.permission)
.then(() => {
res.set(200).send();
next();
})
.catch((e) => {
res.status(400).send(`Error while adding to roles: ${e}`);
});

next();
// Call https://search-api.epfl.ch/api/ldap?q=228271, if the answer is
// empty then sciper unknown, otherwise add it in userDB
});
Expand Down
14 changes: 13 additions & 1 deletion web/frontend/src/language/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
"notLoggedInActionText3": " um diese Aktionen durchzuführen.",
"loginCallback": "Wir fahren mit der Authentifizierung fort. Sie sollten weitergeleitet werden...",
"logout": "Abmeldung",
"changeId": "SCIPER ändern",
"changeIdTitle": "Login mit einer anderen SCIPER id",
"changeIdDialog": "Mit welcher SCIPER id wollen Sie sich einloggen?",
"changeIdContinue": "Login",
"changeIdPlaceholder": "SCIPER",
"namePlaceHolder": "Geben Sie den Namen ein",
"addCandidate": "Einen Kandidaten hinzufügen",
"addUser": "Benutzer hinzufügen",
Expand All @@ -76,6 +81,7 @@
"add": "Hinzufügen",
"exportJSON": "Exportieren als JSON",
"delete": "Löschen",
"addVoters": "Neue WählerInnen",
"combineShares": "Aktien zusammenlegen",
"createElec": "Formular erstellen",
"clearForm": "Das Formular löschen",
Expand Down Expand Up @@ -122,7 +128,8 @@
"open": "Offen",
"close": "Schließen",
"cancel": "Abbrechen",
"canceled": "Abgesagt",
"confirm": "OK",
"canceled": "Abgebrochen",
"action": "Aktion",
"login": "Login",
"loggedIn": "Sie sind eingeloggt. ",
Expand Down Expand Up @@ -226,6 +233,11 @@
"end": "Das Ende",
"save": "Speichern Sie",
"contributors": "Our contributors",
"addVotersDialog": "Wähler hinzufügen",
"addVotersDialogSuccess": "Wähler hinzugefügt",
"votersAdded": "Folgende Wähler wurden hinzugefügt:",
"inputAddVoters": "Für jede wahlberechtigte Person, fügen Sie die SCIPER Nummer auf eine neue Linie hinzu",
"addVotersConfirm": "Hinzufügen",
"nodeSetup": "Einrichtung des Knotens",
"inputNodeSetup": "Wählen Sie den Knoten aus, auf dem die Einrichtung beginnen soll:",
"inputProxyAddressError": "Fehler: Die Adresse eines Proxys kann nicht leer sein.",
Expand Down
12 changes: 12 additions & 0 deletions web/frontend/src/language/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@
"notLoggedInActionText3": " to perform these actions.",
"loginCallback": "We are proceeding with the authentication. You should be redirected...",
"logout": "Logout",
"changeId": "Change SCIPER",
"changeIdTitle": "Login with another SCIPER",
"changeIdDialog": "Enter the SCIPER you want to login with",
"changeIdContinue": "Login",
"changeIdPlaceholder": "SCIPER",
"namePlaceHolder": "Enter the name",
"addCandidate": "Add a candidate",
"addUser": "Add user",
Expand All @@ -75,6 +80,7 @@
"add": "Add",
"exportJSON": "Export as JSON",
"delete": "Delete",
"addVoters": "Add voters",
"combineShares": "Combine shares",
"createElec": "Create form",
"clearForm": "Clear form",
Expand Down Expand Up @@ -121,6 +127,7 @@
"open": "Open",
"close": "Close",
"cancel": "Cancel",
"confirm": "OK",
"canceled": "Canceled",
"action": "Action",
"login": "Login",
Expand All @@ -139,6 +146,7 @@
"initializing": "Initializing...",
"settingUp": "Setting up...",
"statusSetup": "Setup",
"addVotersConfirm": "Add voters",
"setupNode": "Setup Node",
"statusOpen": "Open",
"failed": "Failed",
Expand Down Expand Up @@ -228,6 +236,10 @@
"de": "🇩🇪 German",
"save": "Save",
"contributors": "Our contributors",
"addVotersDialog": "Adding voters",
"addVotersDialogSuccess": "Voters added",
"votersAdded": "The following voters have been added:",
"inputAddVoters": "For every voter, add their SCIPER on a new line",
"nodeSetup": "Node setup",
"inputNodeSetup": "Choose which node to start the setup on:",
"inputProxyAddressError": "Error: the address of a proxy cannot be empty.",
Expand Down
12 changes: 12 additions & 0 deletions web/frontend/src/language/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
"notLoggedInActionText3": " pour effectuer ces actions.",
"loginCallback": "Nous procédons à l'authentification. Vous devriez être redirigé...",
"logout": "Déconnexion",
"changeId": "Changer le SCIPER",
"changeIdTitle": "Login sous un autre SCIPER",
"changeIdDialog": "Entrez le numéro SCIPER sous lequel vous voulez vous connecter",
"changeIdContinue": "Login",
"changeIdPlaceholder": "SCIPER",
"namePlaceHolder": "Entrer le nom",
"addCandidate": "Ajouter un candidat",
"addUser": "Ajouter un utilisateur",
Expand All @@ -76,6 +81,7 @@
"add": "Ajouter",
"exportJSON": "Exporter en JSON",
"delete": "Supprimer",
"addVoters": "Ajouter électeur·rice·s",
"combineShares": "Combiner les actions",
"createElec": "Créer un sondage",
"clearForm": "Effacer un sondage",
Expand Down Expand Up @@ -122,6 +128,7 @@
"open": "Ouvrir",
"close": "Fermer",
"cancel": "Annuler",
"confirm": "OK",
"canceled": "Annulé",
"action": "Action",
"login": "Connexion",
Expand Down Expand Up @@ -226,6 +233,11 @@
"end": "Fin",
"save": "Enregistrer",
"contributors": "Nos contributeurs",
"addVotersDialog": "Ajouter des électeurs",
"addVotersDialogSuccess": "Électeurs ajoutés",
"votersAdded": "Les électeurs suivants ont été ajouté:",
"inputAddVoters": "Saisissez le SCIPER de chaque électeur·rice·s sur une ligne séparée",
"addVotersConfirm": "Ajout SCIPERs",
"nodeSetup": "Configuration du noeud",
"inputNodeSetup": "Choisi un noeud pour commencer la configuration dessus:",
"inputProxyAddressError": "Erreur: l'adresse d'un proxy ne peut pas être vide.",
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
Loading

0 comments on commit 275862a

Please sign in to comment.