Skip to content

Commit 0a6ddfa

Browse files
authored
Replace simple admin UI with a simple UI view (#1446)
NOTES: * This is a continuous WIP e.g. there will be a lot more in stages when there is available time. We need a more user friendly UI though right now. * All columns are to ensure integrity and facilitate easier bug checking. TODO: * Pagination * Possible sort-able column headers * Search-ability * Revoke in `modifySessions.js` and enable with strict logging *(hopefully)* * `authedUser` filter * Possible modified integration with `Session` in Preferences for GDPR Post #1203 ... related to #604 Auto-merge
1 parent 55fdcb0 commit 0a6ddfa

File tree

4 files changed

+180
-54
lines changed

4 files changed

+180
-54
lines changed

controllers/admin.js

+89-54
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ var isDbg = require('../libs/debug').isDbg;
88
//
99

1010
//--- Dependency inclusions
11-
var async = require('async');
1211
var exec = require('child_process').exec;
13-
var git = require('git-rev');
12+
13+
var async = require('async');
1414
var _ = require('underscore');
15+
var git = require('git-rev');
1516

1617
//--- Model inclusions
1718
var Comment = require('../models/comment').Comment;
@@ -345,86 +346,120 @@ exports.adminProcessCloneView = function (aReq, aRes, aNext) {
345346
}
346347

347348
exports.adminSessionActiveView = function (aReq, aRes, aNext) {
348-
if (!userIsAdmin(aReq)) {
349+
function preRender() {
350+
}
351+
352+
function render() {
353+
aRes.render('pages/adminSessionListPage', options);
354+
}
355+
356+
function asyncComplete(aErr) {
357+
if (aErr) {
358+
aNext();
359+
return;
360+
}
361+
362+
preRender();
363+
render();
364+
}
365+
366+
//
367+
var options = {};
368+
var authedUser = aReq.session.user;
369+
var tasks = [];
370+
371+
var store = aReq.sessionStore;
372+
var sessionColl = store.db.collection('sessions');
373+
374+
// Session
375+
options.authedUser = authedUser = modelParser.parseUser(authedUser);
376+
options.isMod = authedUser && authedUser.isMod;
377+
options.isAdmin = authedUser && authedUser.isAdmin;
378+
379+
if (!options.isAdmin) {
349380
statusCodePage(aReq, aRes, aNext, {
350381
statusCode: 403,
351382
statusMessage: 'This page is only accessible by admins.'
352383
});
353384
return;
354385
}
355386

356-
var store = aReq.sessionStore;
357-
var sessionColl = store.db.collection('sessions');
358387

359-
sessionColl.find({
360-
}, function (aErr, aUserSessions) {
361-
if (aErr) {
362-
statusCodePage(aReq, aRes, aNext, {
363-
statusCode: 520,
364-
statusMessage: 'Unknown error with `sessionColl.find`.'
365-
});
366-
return;
367-
}
388+
// Page metadata
389+
pageMetadata(options, ['Sessions', 'Admin']);
368390

369-
if (!aUserSessions) {
370-
statusCodePage(aReq, aRes, aNext, {
371-
statusCode: 200,
372-
statusMessage: 'No sessions'
373-
});
374-
return;
375-
}
391+
//--- Tasks
376392

377-
aUserSessions.toArray(function (aErr, aSessionsData) {
378-
var options = {};
379-
options.namedCount = 0;
380-
options.rawCount = 0;
381-
options.session = [];
393+
tasks.push(function (aCallback) {
382394

395+
sessionColl.find({
396+
}, function (aErr, aUserSessions) {
383397
if (aErr) {
384-
// We want to know what the error value is in logging review
385-
console.error('Unknown error with `toArray`.\n' + aErr);
386-
387398
statusCodePage(aReq, aRes, aNext, {
388399
statusCode: 520,
389-
statusMessage: 'Unknown error with `toArray`.'
400+
statusMessage: 'Unknown error with `sessionColl.find`.'
390401
});
391402
return;
392403
}
393404

394-
aSessionsData.forEach(function (aElement, aIndex) {
395-
var data = JSON.parse(aElement.session);
405+
if (!aUserSessions) {
406+
statusCodePage(aReq, aRes, aNext, {
407+
statusCode: 200,
408+
statusMessage: 'No sessions'
409+
});
410+
return;
411+
}
396412

397-
if (data && data.user) {
398-
options.session.push({
399-
_id: aElement._id,
400-
name: data.user.name,
401-
cookie: data.cookie
402-
});
413+
aUserSessions.toArray(function (aErr, aSessionsData) {
414+
options.sessionList = [];
403415

404-
options.namedCount++;
416+
if (aErr) {
417+
// We want to know what the error value is in logging review
418+
console.error('Unknown error with `toArray`.\n' + aErr);
405419

406-
} else if (data && !data.user) {
407-
options.session.push({
408-
_id: aElement._id,
409-
expires: data.cookie.expires,
410-
data: data
420+
statusCodePage(aReq, aRes, aNext, {
421+
statusCode: 520,
422+
statusMessage: 'Unknown error with `toArray`.'
411423
});
424+
return;
412425
}
413-
options.rawCount++;
414-
});
415426

416-
// Sort the sessions
417-
options.session = _.sortBy(options.session, function (aSession) {
418-
return (aSession.name) ? aSession.name.toLowerCase() : aSession.name;
419-
});
427+
aSessionsData.forEach(function (aElement, aIndex) {
428+
var data = JSON.parse(aElement.session);
429+
var obj = null;
430+
431+
if (data) {
432+
obj = {
433+
_id: aElement._id,
434+
name: (data.user ? data.user.name : data.username),
435+
userPageUrl: (data.user ? data.user.userPageUrl : null),
436+
cookie: {
437+
expires: (data.cookie.expires ? new Date(data.cookie.expires) : false),
438+
secure: data.cookie.secure,
439+
httpOnly: data.cookie.httpOnly,
440+
sameSite: data.cookie.sameSite,
441+
sameSiteStrict: data.cookie.sameSite === 'strict',
442+
sameSiteLax: data.cookie.sameSite === 'lax' ,
443+
}
444+
};
445+
modelParser.parseDateProperty(obj.cookie, 'expires');
446+
options.sessionList.push(obj);
447+
}
448+
});
420449

421-
// Currently output in simplified JSON
422-
aRes.set('Content-Type', 'application/json; charset=UTF-8');
423-
aRes.write(JSON.stringify(options, null, isPro ? '' : ' '));
424-
aRes.end();
450+
// Sort the sessions for now
451+
options.sessionList = _.sortBy(options.sessionList, function (aSession) {
452+
return (aSession.name) ? aSession.name.toLowerCase() : aSession.name;
453+
});
425454

455+
aCallback();
456+
});
426457
});
458+
427459
});
460+
461+
//---
462+
async.parallel(tasks, asyncComplete);
428463
};
429464

430465
// View everything about current deployed `./package.json`

libs/modelParser.js

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ var parseDateProperty = function (aObj, aKey) {
9999
}
100100
}
101101
};
102+
exports.parseDateProperty = parseDateProperty;
102103

103104
// Parse persisted model data and return a new object
104105
// with additional generated fields used in view templates.

views/includes/sessionList.html

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<table class="table table-hover table-condensed">
2+
<thead>
3+
<tr>
4+
<th>Name</th>
5+
<th class="text-center">expires</th>
6+
<th class="text-center">secure</th>
7+
<th class="text-center">httpOnly</th>
8+
<th class="text-center">sameSite</th>
9+
<th>Action</th>
10+
</tr>
11+
</thead>
12+
<tbody>
13+
{{#sessionList}}
14+
<tr class="tr-link">
15+
<td>
16+
<b>
17+
{{#userPageUrl}}<a href="{{{userPageUrl}}}" class="tr-link-a">{{name}}</a>{{/userPageUrl}}
18+
{{^userPageUrl}}{{name}}{{/userPageUrl}}
19+
</b>
20+
</td>
21+
<td class="text-center td-fit">
22+
{{#cookie.expires}}<time datetime="{{cookie.expiresISOFormat}}" title="{{cookie.expires}}">{{cookie.expiresHumanized}}</time>{{/cookie.expires}}
23+
{{^cookie.expires}}&infin;{{/cookie.expires}}
24+
</td>
25+
<td class="text-center td-fit">
26+
<span class="fa fa-{{^cookie.secure}}un{{/cookie.secure}}lock"></span>
27+
</td>
28+
<td class="text-center td-fit">
29+
<span class="fa fa-{{#cookie.httpOnly}}check{{/cookie.httpOnly}}{{^cookie.httpOnly}}times{{/cookie.httpOnly}}"></span>
30+
</td>
31+
<td class="text-center td-fit">
32+
{{#cookie.sameSiteStrict}}<span class="fa fa-circle"></span>{{/cookie.sameSiteStrict}}
33+
{{#cookie.sameSiteLax}}<span class="fa fa-adjust"></span>{{/cookie.sameSiteLax}}
34+
{{^cookie.sameSite}}<span class="fa fa-circle-o"></span>{{/cookie.sameSite}}
35+
</td>
36+
<td class="td-fit">
37+
<button type="button" class="btn btn-sm btn-success disabled" aria-label="Left Align">
38+
<span class="fa fa-sign-out" aria-hidden="true"> Sign Out</span>
39+
</button>
40+
</td>
41+
</tr>
42+
{{/sessionList}}
43+
</tbody>
44+
</table>

views/pages/adminSessionListPage.html

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>{{title}}</title>
5+
{{> includes/head.html }}
6+
</head>
7+
<body>
8+
{{> includes/header.html }}
9+
<div class="container-fluid">
10+
<div class="row">
11+
<div class="col-sm-8">
12+
<h2 class="page-heading">
13+
Sessions
14+
</h2>
15+
{{#paginationRendered}}
16+
<div class="text-center collapse">
17+
{{{paginationRendered}}}
18+
</div>
19+
{{/paginationRendered}}
20+
<div class="panel panel-default">
21+
{{> includes/sessionList.html }}
22+
</div>
23+
{{#paginationRendered}}
24+
<div class="text-center">
25+
{{{paginationRendered}}}
26+
</div>
27+
{{/paginationRendered}}
28+
</div>
29+
<div class="col-sm-4">
30+
{{> includes/searchBarPanel.html }}
31+
{{#isFlagged}}
32+
{{#isAdmin}}
33+
{{> includes/flagAdminToolFlaggedFilters.html }}
34+
{{/isAdmin}}
35+
{{/isFlagged}}
36+
</div>
37+
</div>
38+
</div>
39+
{{> includes/footer.html }}
40+
{{#paginationRendered}}
41+
{{> includes/scripts/showTopPagination.html }}
42+
{{/paginationRendered}}
43+
{{> includes/scripts/tableTrLinkScript.html }}
44+
{{> includes/scripts/formControlClear.html }}
45+
</body>
46+
</html>

0 commit comments

Comments
 (0)