From 2ec6e7650f8ae1534627533c486b12514a741603 Mon Sep 17 00:00:00 2001 From: Adam Black Date: Tue, 25 May 2021 20:36:35 +0100 Subject: [PATCH] Wrote test for /retropilot/0/useradmin Converted a few more routes to return json New config option: flag: { useUserAdminApi: BOOLEAN } this will load the userAdminApi routes into the express app. --- config.sample.js | 8 ++-- package.json | 1 + routes/userAdminApi.js | 77 ++++++++++---------------------- server.js | 4 +- test/routes/userAdminApi.test.js | 46 +++++++++++++++++++ test/test.js | 4 +- 6 files changed, 82 insertions(+), 58 deletions(-) create mode 100644 test/routes/userAdminApi.test.js diff --git a/config.sample.js b/config.sample.js index 139ceac..c262685 100644 --- a/config.sample.js +++ b/config.sample.js @@ -20,8 +20,6 @@ var config = { smtpPassword: "", smtpFrom: "no-reply@retropilot.org", - SENDGRID_API_KEY: '', - baseUrl: 'http://192.168.1.165:3000/', // base url of the retropilot server baseUploadUrl: 'http://192.168.1.165:3000/backend/post_upload', // base url sent to devices for POSTing drives & logs @@ -35,7 +33,11 @@ var config = { deviceDriveExpirationDays: 30, - welcomeMessage: `<><><><><><><><><><><><><><><><><><><><><><>
2021 RetroPilot` + welcomeMessage: `<><><><><><><><><><><><><><><><><><><><><><>
2021 RetroPilot`, + + flags: { + useUserAdminApi: false, + } }; module.exports = config; diff --git a/package.json b/package.json index bc95aad..893549c 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "license": "ISC", "dependencies": { "@commaai/log_reader": "^0.8.0", + "@sendgrid/client": "^7.4.3", "chai": "^4.3.4", "chai-http": "^4.3.0", "cookie-parser": "^1.4.5", diff --git a/routes/userAdminApi.js b/routes/userAdminApi.js index b8ccb5f..c876035 100644 --- a/routes/userAdminApi.js +++ b/routes/userAdminApi.js @@ -1,8 +1,6 @@ const router = require('express').Router(); const bodyParser = require('body-parser'); const crypto = require('crypto'); -const htmlspecialchars = require('htmlspecialchars'); -const dirTree = require("directory-tree"); const cookieParser = require('cookie-parser'); const config = require('./../config'); @@ -23,7 +21,7 @@ let controllers; let logger; -router.post('/useradmin/auth', bodyParser.urlencoded({extended: true}), runAsyncWrapper(async (req, res) => { +router.post('/retropilot/0/useradmin/auth', bodyParser.urlencoded({extended: true}), runAsyncWrapper(async (req, res) => { const account = await models.__db.get('SELECT * FROM accounts WHERE email = ? AND password = ?', req.body.email, crypto.createHash('sha256').update(req.body.password + config.applicationSalt).digest('hex')); if (!account || account.banned) { @@ -34,13 +32,13 @@ router.post('/useradmin/auth', bodyParser.urlencoded({extended: true}), runAsync })) -router.get('/useradmin/signout', runAsyncWrapper(async (req, res) => { +router.get('/retropilot/0/useradmin/signout', runAsyncWrapper(async (req, res) => { res.clearCookie('session'); return res.json({success: true}); })) -router.get('/useradmin', runAsyncWrapper(async (req, res) => { +router.get('/retropilot/0/useradmin', runAsyncWrapper(async (req, res) => { const accounts = await models.__db.get('SELECT COUNT(*) AS num FROM accounts'); const devices = await models.__db.get('SELECT COUNT(*) AS num FROM devices'); const drives = await models.__db.get('SELECT COUNT(*) AS num FROM drives'); @@ -98,18 +96,12 @@ router.get('/retropilot/0/register/verify/:token', bodyParser.urlencoded({extend return res.json({success: false, msg: 'contact server admin'}).status(500); } - - - - })); +/* router.post('/useradmin/register/token', bodyParser.urlencoded({extended: true}), runAsyncWrapper(async (req, res) => { - - const email = req.body.email; - if (!config.allowAccountRegistration) { res.send('Unauthorized.').status(401); return; @@ -205,63 +197,41 @@ router.get('/useradmin/register', runAsyncWrapper(async (req, res) => { `); })) +*/ - -router.get('/useradmin/overview', runAsyncWrapper(async (req, res) => { - const account = await controllers.authentication.getAuthenticatedAccount(req, res); +router.get('/retropilot/0/overview', runAsyncWrapper(async (req, res) => { + let account = await controllers.authentication.getAuthenticatedAccount(req, res); if (account == null) { - res.redirect('/useradmin?status=' + encodeURIComponent('Invalid or expired session')); + res.send({success: false, data: {session: false}}) + return; } const devices = await models.__db.all('SELECT * FROM devices WHERE account_id = ? ORDER BY dongle_id ASC', account.id) - var response = '

Welcome To The RetroPilot Server Dashboard!

' + - - `

Account Overview

- Account: #` + account.id + `
- Email: ` + account.email + `
- Created: ` + controllers.helpers.formatDate(account.created) + `

- Devices:
- - - `; - - for (var i in devices) { - response += ''; - } - response += `
dongle_iddevice_typecreatedlast_pingstorage_used
' + devices[i].dongle_id + '' + devices[i].device_type + '' + controllers.helpers.formatDate(devices[i].created) + '' + controllers.helpers.formatDate(devices[i].last_ping) + '' + devices[i].storage_used + ' MB
-
-
-

Pair New Devices

- * To pair a new device, first have it auto-register on this server.
Then scan the QR Code and paste the Device Token below.

- ` + (req.query.linkstatus !== undefined ? '
' + htmlspecialchars(req.query.linkstatus) + '

' : '') + ` -
- - -


-
- Sign Out`; - - res.status(200); - res.send(response); + // TODO implement a _safe_ get account for these use cases to allow for data to be stripped prior to sending to the client. + delete(account.email_verify_token); + res.json({ + success: true, + data: { + account: account, + devices: devices, + } + }).status(200) })) router.get('/useradmin/unpair_device/:dongleId', runAsyncWrapper(async (req, res) => { const account = await controllers.authentication.getAuthenticatedAccount(req, res); if (account == null) { - res.redirect('/useradmin?status=' + encodeURIComponent('Invalid or expired session')); - return; + return res.json({success: false, data: {session: false}}).status(403) } const device = await models.__db.get('SELECT * FROM devices WHERE account_id = ? AND dongle_id = ?', account.id, req.params.dongleId); if (device == null) { - res.status(400); - res.send('Unauthorized.'); - return; + return res.json({success: false}).status(400); } const result = await models.__db.run( @@ -270,9 +240,10 @@ router.get('/useradmin/unpair_device/:dongleId', runAsyncWrapper(async (req, res req.params.dongleId ); - res.redirect('/useradmin/overview'); -})), + res.json({success: true, data: {unlink: true}}) +})) +/* router.post('/useradmin/pair_device', bodyParser.urlencoded({extended: true}), runAsyncWrapper(async (req, res) => { const account = await controllers.authentication.getAuthenticatedAccount(req, res); @@ -571,7 +542,7 @@ router.get('/useradmin/drive/:dongleId/:driveIdentifier', runAsyncWrapper(async res.send(response); })) - +*/ module.exports = (_models, _controllers, _logger) => { models = _models; diff --git a/server.js b/server.js index a896c1c..a8fd5fe 100644 --- a/server.js +++ b/server.js @@ -53,7 +53,9 @@ const web = async () => { routers = routers(models, controllers, logger) app.use(routers.api); app.use(routers.useradmin); - app.use(routers.useradminapi); + + if (config.flags.useUserAdminApi) app.use(routers.useradminapi); + app.use(cors()); app.use(cookieParser(config.applicationSalt)) diff --git a/test/routes/userAdminApi.test.js b/test/routes/userAdminApi.test.js new file mode 100644 index 0000000..6d4a354 --- /dev/null +++ b/test/routes/userAdminApi.test.js @@ -0,0 +1,46 @@ +const request = require('supertest'); +const dummyGenerator = require('./../dummyGenerator'); +let app; + + + +module.exports = (server) => { + app = server; + + describe('/api', function() { + it('Load general server stats', function (done) { + request(server) + .get('/retropilot/0/useradmin') + .expect('Content-Type', /json/) + .expect(200) + .expect((req) => { + const body = req.body; + + try { + if ( + body.hasOwnProperty('success') && body.success === true && + body.hasOwnProperty('data') && + body.data.hasOwnProperty('serverStats') && + body.data.serverStats.hasOwnProperty('config') && + typeof body.data.serverStats.config.registerAllowed === "boolean" && + typeof body.data.serverStats.config.welcomeMessage === "string" && + typeof body.data.serverStats['accounts'] === "number" && + typeof body.data.serverStats['devices'] === "number" && + typeof body.data.serverStats['drives'] === "number" && + (typeof body.data.serverStats['storageUsed'] === "number" || body.data.serverStats['storageUsed'] === "Unsupported Platform")) + { + return true; + } else { + throw new Error('Invalid returned parameters in GET /retropilot/0/useradmin') + } + } catch (exception) { + throw new Error('Invalid returned parameters in GET /retropilot/0/useradmin ') + } + }) + .end(done) + }); + + + }); + +} diff --git a/test/test.js b/test/test.js index 828575a..e880eee 100644 --- a/test/test.js +++ b/test/test.js @@ -1,4 +1,5 @@ var server = require('./../server') +const config = require('./../config'); var request = require('supertest'); // TODO better way to only run tests once server is up @@ -19,4 +20,5 @@ describe('loading express', function () { require('./routes/api.test')(server); -require('./routes/useradmin.test')(server); \ No newline at end of file +require('./routes/useradmin.test')(server); +if (config.flags.useUserAdminApi) require('./routes/userAdminApi.test')(server); \ No newline at end of file