Skip to content

Commit 2aa1565

Browse files
authored
Merge pull request #304 from dedis/refactor/makes-session-management-a-bit-more-readable
Refactor: makes session management a bit more readable
2 parents d9fdaf7 + 01c4478 commit 2aa1565

File tree

5 files changed

+92
-90
lines changed

5 files changed

+92
-90
lines changed

web/backend/src/Server.ts

+42-48
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,11 @@ const PERMISSIONS = {
3131
},
3232
};
3333

34-
// store is used to store the session
35-
const store = new MemoryStore({
34+
const sessionStore = new MemoryStore({
3635
checkPeriod: 86400000, // prune expired entries every 24h
3736
});
3837

39-
// Keeps an in-memory mapping between a SCIPER (userid) and its opened session
38+
// Keeps an in-memory mapping between a SCIPER (userId) and its opened session
4039
// IDs. Needed to invalidate the sessions of a user when its role changes. The
4140
// value is a set of sessions IDs.
4241
const sciper2sess = new Map<number, Set<string>>();
@@ -65,13 +64,13 @@ async function initEnforcer() {
6564
return newEnforcer('src/model.conf', dbAdapter);
6665
}
6766

68-
const port = process.env.PORT || 5000;
67+
const serveOnPort = process.env.PORT || 5000;
6968
Promise.all([initEnforcer()])
7069
.then((createdEnforcer) => {
7170
[authEnforcer] = createdEnforcer;
7271
console.log(`🛡 Casbin authorization service loaded`);
73-
app.listen(port);
74-
console.log(`🚀 App is listening on port ${port}`);
72+
app.listen(serveOnPort);
73+
console.log(`🚀 App is listening on port ${serveOnPort}`);
7574
})
7675
.catch((err) => {
7776
console.error('❌ failed to start:', err);
@@ -82,10 +81,11 @@ function isAuthorized(sciper: number | undefined, subject: string, action: strin
8281
}
8382

8483
declare module 'express-session' {
84+
// This overrides express-session
8585
export interface SessionData {
86-
userid: number;
87-
firstname: string;
88-
lastname: string;
86+
userId: number;
87+
firstName: string;
88+
lastName: string;
8989
}
9090
}
9191

@@ -100,7 +100,7 @@ app.use(
100100
saveUninitialized: true,
101101
cookie: { maxAge: oneDay },
102102
resave: false,
103-
store: store,
103+
store: sessionStore,
104104
})
105105
);
106106

@@ -113,7 +113,7 @@ app.use(express.urlencoded({ extended: true }));
113113
// app.use((req, res, next) => {
114114
// const begin = req.url.split('?')[0];
115115
// let role = 'everyone';
116-
// if (req.session.userid && req.session.role) {
116+
// if (req.session.userId && req.session.role) {
117117
// role = req.session.role;
118118
// }
119119

@@ -166,11 +166,11 @@ app.get('/api/control_key', (req, res) => {
166166
const lastname = response.data.split('\nname=')[1].split('\n')[0];
167167
const firstname = response.data.split('\nfirstname=')[1].split('\n')[0];
168168

169-
req.session.userid = parseInt(sciper, 10);
170-
req.session.lastname = lastname;
171-
req.session.firstname = firstname;
169+
req.session.userId = parseInt(sciper, 10);
170+
req.session.lastName = lastname;
171+
req.session.firstName = firstname;
172172

173-
const sciperSessions = sciper2sess.get(req.session.userid) || new Set<string>();
173+
const sciperSessions = sciper2sess.get(req.session.userId) || new Set<string>();
174174
sciperSessions.add(req.sessionID);
175175
sciper2sess.set(sciper, sciperSessions);
176176

@@ -184,17 +184,17 @@ app.get('/api/control_key', (req, res) => {
184184

185185
// This endpoint serves to log out from the app by clearing the session.
186186
app.post('/api/logout', (req, res) => {
187-
if (req.session.userid === undefined) {
187+
if (req.session.userId === undefined) {
188188
res.status(400).send('not logged in');
189189
}
190190

191-
const { userid } = req.session;
191+
const { userId } = req.session;
192192

193193
req.session.destroy(() => {
194-
const a = sciper2sess.get(userid as number);
194+
const a = sciper2sess.get(userId as number);
195195
if (a !== undefined) {
196196
a.delete(req.sessionID);
197-
sciper2sess.set(userid as number, a);
197+
sciper2sess.set(userId as number, a);
198198
}
199199
res.redirect('/');
200200
});
@@ -224,24 +224,18 @@ function setMapAuthorization(list: string[][]): Map<String, Array<String>> {
224224
// be logged into react. This endpoint serves to send to the client (actually to react)
225225
// the information of the current user.
226226
app.get('/api/personal_info', (req, res) => {
227-
authEnforcer.getFilteredPolicy(0, String(req.session.userid)).then((AuthRights) => {
227+
authEnforcer.getFilteredPolicy(0, String(req.session.userId)).then((AuthRights) => {
228228
res.set('Access-Control-Allow-Origin', '*');
229-
if (req.session.userid) {
229+
if (req.session.userId) {
230230
res.json({
231-
sciper: req.session.userid,
232-
lastname: req.session.lastname,
233-
firstname: req.session.firstname,
234-
islogged: true,
231+
sciper: req.session.userId,
232+
lastName: req.session.lastName,
233+
firstName: req.session.firstName,
234+
isLoggedIn: true,
235235
authorization: Object.fromEntries(setMapAuthorization(AuthRights)),
236236
});
237237
} else {
238-
res.json({
239-
sciper: 0,
240-
lastname: '',
241-
firstname: '',
242-
islogged: false,
243-
authorization: {},
244-
});
238+
res.status(401).send();
245239
}
246240
});
247241
});
@@ -252,7 +246,7 @@ app.get('/api/personal_info', (req, res) => {
252246
// This call allows a user that is admin to get the list of the people that have
253247
// a special role (not a voter).
254248
app.get('/api/user_rights', (req, res) => {
255-
if (!isAuthorized(req.session.userid, PERMISSIONS.SUBJECTS.ROLES, PERMISSIONS.ACTIONS.LIST)) {
249+
if (!isAuthorized(req.session.userId, PERMISSIONS.SUBJECTS.ROLES, PERMISSIONS.ACTIONS.LIST)) {
256250
res.status(400).send('Unauthorized - only admins allowed');
257251
return;
258252
}
@@ -266,7 +260,7 @@ app.get('/api/user_rights', (req, res) => {
266260

267261
// This call (only for admins) allow an admin to add a role to a voter.
268262
app.post('/api/add_role', (req, res, next) => {
269-
if (!isAuthorized(req.session.userid, PERMISSIONS.SUBJECTS.ROLES, PERMISSIONS.ACTIONS.ADD)) {
263+
if (!isAuthorized(req.session.userId, PERMISSIONS.SUBJECTS.ROLES, PERMISSIONS.ACTIONS.ADD)) {
270264
res.status(400).send('Unauthorized - only admins allowed');
271265
return;
272266
}
@@ -286,7 +280,7 @@ app.post('/api/add_role', (req, res, next) => {
286280
// This call (only for admins) allow an admin to remove a role to a user.
287281

288282
app.post('/api/remove_role', (req, res, next) => {
289-
if (!isAuthorized(req.session.userid, PERMISSIONS.SUBJECTS.ROLES, PERMISSIONS.ACTIONS.REMOVE)) {
283+
if (!isAuthorized(req.session.userId, PERMISSIONS.SUBJECTS.ROLES, PERMISSIONS.ACTIONS.REMOVE)) {
290284
res.status(400).send('Unauthorized - only admins allowed');
291285
return;
292286
}
@@ -302,7 +296,7 @@ app.post('/api/remove_role', (req, res, next) => {
302296
// ---
303297
const proxiesDB = lmdb.open<string, string>({ path: `${process.env.DB_PATH}proxies` });
304298
app.post('/api/proxies', (req, res) => {
305-
if (!isAuthorized(req.session.userid, PERMISSIONS.SUBJECTS.PROXIES, PERMISSIONS.ACTIONS.POST)) {
299+
if (!isAuthorized(req.session.userId, PERMISSIONS.SUBJECTS.PROXIES, PERMISSIONS.ACTIONS.POST)) {
306300
res.status(400).send('Unauthorized - only admins and operators allowed');
307301
return;
308302
}
@@ -317,7 +311,7 @@ app.post('/api/proxies', (req, res) => {
317311
});
318312

319313
app.put('/api/proxies/:nodeAddr', (req, res) => {
320-
if (!isAuthorized(req.session.userid, PERMISSIONS.SUBJECTS.PROXIES, PERMISSIONS.ACTIONS.PUT)) {
314+
if (!isAuthorized(req.session.userId, PERMISSIONS.SUBJECTS.PROXIES, PERMISSIONS.ACTIONS.PUT)) {
321315
res.status(400).send('Unauthorized - only admins and operators allowed');
322316
return;
323317
}
@@ -354,7 +348,7 @@ app.put('/api/proxies/:nodeAddr', (req, res) => {
354348
});
355349

356350
app.delete('/api/proxies/:nodeAddr', (req, res) => {
357-
if (!isAuthorized(req.session.userid, PERMISSIONS.SUBJECTS.PROXIES, PERMISSIONS.ACTIONS.DELETE)) {
351+
if (!isAuthorized(req.session.userId, PERMISSIONS.SUBJECTS.PROXIES, PERMISSIONS.ACTIONS.DELETE)) {
358352
res.status(400).send('Unauthorized - only admins and operators allowed');
359353
return;
360354
}
@@ -507,13 +501,13 @@ function sendToDela(dataStr: string, req: express.Request, res: express.Response
507501
// Secure /api/evoting to admins and operators
508502
app.put('/api/evoting/authorizations', (req, res) => {
509503
if (
510-
!isAuthorized(req.session.userid, PERMISSIONS.SUBJECTS.ELECTION, PERMISSIONS.ACTIONS.CREATE)
504+
!isAuthorized(req.session.userId, PERMISSIONS.SUBJECTS.ELECTION, PERMISSIONS.ACTIONS.CREATE)
511505
) {
512506
res.status(400).send('Unauthorized');
513507
return;
514508
}
515509
const { FormID } = req.body;
516-
authEnforcer.addPolicy(String(req.session.userid), FormID, PERMISSIONS.ACTIONS.OWN);
510+
authEnforcer.addPolicy(String(req.session.userId), FormID, PERMISSIONS.ACTIONS.OWN);
517511
});
518512

519513
// https://stackoverflow.com/a/1349426
@@ -528,7 +522,7 @@ function makeid(length: number) {
528522
}
529523
app.put('/api/evoting/forms/:formID', (req, res, next) => {
530524
const { formID } = req.params;
531-
if (!isAuthorized(req.session.userid, formID, PERMISSIONS.ACTIONS.OWN)) {
525+
if (!isAuthorized(req.session.userId, formID, PERMISSIONS.ACTIONS.OWN)) {
532526
res.status(400).send('Unauthorized');
533527
return;
534528
}
@@ -537,7 +531,7 @@ app.put('/api/evoting/forms/:formID', (req, res, next) => {
537531

538532
app.post('/api/evoting/services/dkg/actors', (req, res, next) => {
539533
const { FormID } = req.body;
540-
if (!isAuthorized(req.session.userid, FormID, PERMISSIONS.ACTIONS.OWN)) {
534+
if (!isAuthorized(req.session.userId, FormID, PERMISSIONS.ACTIONS.OWN)) {
541535
res.status(400).send('Unauthorized');
542536
return;
543537
}
@@ -548,23 +542,23 @@ app.post('/api/evoting/services/dkg/actors', (req, res, next) => {
548542
});
549543
app.use('/api/evoting/services/dkg/actors/:formID', (req, res, next) => {
550544
const { formID } = req.params;
551-
if (!isAuthorized(req.session.userid, formID, PERMISSIONS.ACTIONS.OWN)) {
545+
if (!isAuthorized(req.session.userId, formID, PERMISSIONS.ACTIONS.OWN)) {
552546
res.status(400).send('Unauthorized');
553547
return;
554548
}
555549
next();
556550
});
557551
app.use('/api/evoting/services/shuffle/:formID', (req, res, next) => {
558552
const { formID } = req.params;
559-
if (!isAuthorized(req.session.userid, formID, PERMISSIONS.ACTIONS.OWN)) {
553+
if (!isAuthorized(req.session.userId, formID, PERMISSIONS.ACTIONS.OWN)) {
560554
res.status(400).send('Unauthorized');
561555
return;
562556
}
563557
next();
564558
});
565559
app.delete('/api/evoting/forms/:formID', (req, res) => {
566560
const { formID } = req.params;
567-
if (!isAuthorized(req.session.userid, formID, PERMISSIONS.ACTIONS.OWN)) {
561+
if (!isAuthorized(req.session.userId, formID, PERMISSIONS.ACTIONS.OWN)) {
568562
res.status(400).send('Unauthorized');
569563
return;
570564
}
@@ -604,7 +598,7 @@ app.delete('/api/evoting/forms/:formID', (req, res) => {
604598
.status(500)
605599
.send(`failed to proxy request: ${req.method} ${uri} - ${error.message} - ${resp}`);
606600
});
607-
authEnforcer.removePolicy(String(req.session.userid), formID, PERMISSIONS.ACTIONS.OWN);
601+
authEnforcer.removePolicy(String(req.session.userId), formID, PERMISSIONS.ACTIONS.OWN);
608602
});
609603

610604
// This API call is used redirect all the calls for DELA to the DELAs nodes.
@@ -613,7 +607,7 @@ app.delete('/api/evoting/forms/:formID', (req, res) => {
613607
// DELA node To make this work, React has to redirect to this backend all the
614608
// request that needs to go the DELA nodes
615609
app.use('/api/evoting/*', (req, res) => {
616-
if (!req.session.userid) {
610+
if (!req.session.userId) {
617611
res.status(400).send('Unauthorized');
618612
return;
619613
}
@@ -627,7 +621,7 @@ app.use('/api/evoting/*', (req, res) => {
627621
// only needed to allow users to cast multiple ballots, where only the last
628622
// ballot is taken into account. To preserve anonymity the web-backend could
629623
// translate UserIDs to another random ID.
630-
// bodyData.UserID = req.session.userid.toString();
624+
// bodyData.UserID = req.session.userId.toString();
631625
bodyData.UserID = makeid(10);
632626
}
633627

web/frontend/src/index.tsx

+43-35
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as endpoints from 'components/utils/Endpoints';
1313

1414
const flashTimeout = 4000;
1515

16-
// By default we load the mock messages when not in production. This is handy
16+
// By default, we load the mock messages when not in production. This is handy
1717
// because it removes the need to have a backend server.
1818
if (process.env.NODE_ENV !== 'production' && process.env.REACT_APP_NOMOCK !== 'on') {
1919
const { dvotingserver } = require('./mocks/dvotingserver');
@@ -22,8 +22,8 @@ if (process.env.NODE_ENV !== 'production' && process.env.REACT_APP_NOMOCK !== 'o
2222
const arr = new Map<String, Array<String>>();
2323
const defaultAuth = {
2424
isLogged: false,
25-
firstname: '',
26-
lastname: '',
25+
firstName: '',
26+
lastName: '',
2727
authorization: arr,
2828
isAllowed: (subject: string, action: string) => false,
2929
};
@@ -36,8 +36,8 @@ export const AuthContext = createContext<AuthState>(defaultAuth);
3636

3737
export interface AuthState {
3838
isLogged: boolean;
39-
firstname: string;
40-
lastname: string;
39+
firstName: string;
40+
lastName: string;
4141
authorization: Map<String, Array<String>>;
4242
isAllowed: (subject: string, action: string) => boolean;
4343
}
@@ -224,39 +224,47 @@ const AppContainer = () => {
224224
};
225225

226226
async function fetchData() {
227-
try {
228-
const res = await fetch(ENDPOINT_PERSONAL_INFO, req);
229-
230-
if (res.status !== 200) {
231-
const txt = await res.text();
232-
throw new Error(`unexpected status: ${res.status} - ${txt}`);
227+
const response = await fetch(ENDPOINT_PERSONAL_INFO, req);
228+
let result;
229+
switch (response.status) {
230+
case 200: {
231+
result = await response.json();
232+
break;
233+
}
234+
case 401: {
235+
result = {
236+
isLoggedIn: false,
237+
firstName: '',
238+
lastName: '',
239+
authorization: {},
240+
};
241+
break;
242+
}
243+
default: {
244+
const txt = await response.text();
245+
throw new Error(`Unexpected status: ${response.status} - ${txt}`);
233246
}
234-
235-
const result = await res.json();
236-
setAuth({
237-
isLogged: result.islogged,
238-
firstname: result.firstname,
239-
lastname: result.lastname,
240-
authorization: result.islogged ? new Map(Object.entries(result.authorization)) : arr,
241-
isAllowed: function (subject: string, action: string) {
242-
return (
243-
this.authorization.has(subject) &&
244-
this.authorization.get(subject).indexOf(action) !== -1
245-
);
246-
},
247-
});
248-
249-
// wait for the default proxy to be set
250-
await setDefaultProxy();
251-
252-
setContent(<App />);
253-
} catch (e: any) {
254-
setContent(<Failed>{e.toString()}</Failed>);
255-
console.log('error:', e);
256247
}
248+
setAuth({
249+
isLogged: result.isLoggedIn,
250+
firstName: result.firstName,
251+
lastName: result.lastName,
252+
authorization: result.isLoggedIn ? new Map(Object.entries(result.authorization)) : arr,
253+
isAllowed: function (subject: string, action: string) {
254+
return (
255+
this.authorization.has(subject) &&
256+
this.authorization.get(subject).indexOf(action) !== -1
257+
);
258+
},
259+
});
260+
// wait for the default proxy to be set
261+
await setDefaultProxy();
262+
setContent(<App />);
257263
}
258-
259-
fetchData();
264+
fetchData().catch((e) => {
265+
setContent(<Failed>{e.toString()}</Failed>);
266+
console.log('error:', e);
267+
});
260268
}, []);
261269

262270
return (

0 commit comments

Comments
 (0)