move API routes under "/api" path

pull/4/head
Cameron Clough 2022-03-22 13:03:17 +00:00
parent cbe3cf0c2a
commit c3359bb223
No known key found for this signature in database
GPG Key ID: BFB3B74B026ED43F
18 changed files with 162 additions and 237 deletions

View File

@ -7,7 +7,7 @@ import log4js from 'log4js';
import storageController from './controllers/storage';
import athena from './websocket/athena';
import controllers from './controllers';
import routers from './routes';
import routers from './router';
const logger = log4js.getLogger('default');
@ -30,9 +30,8 @@ app.use(cors({
}));
app.use(cookieParser());
app.use(routers.api);
app.use(routers.useradmin);
app.use(routers.authenticationApi);
app.use('/api', routers.api);
app.use('/useradmin', routers.useradmin);
if (process.env.ATHENA_ENABLED) {
const athenaRateLimit = rateLimit({
@ -56,8 +55,6 @@ if (process.env.ATHENA_ENABLED) {
app.use('/favicon.ico', express.static('static/favicon.ico'));
app.use(process.env.BASE_DRIVE_DOWNLOAD_PATH_MAPPING, express.static(process.env.STORAGE_PATH));
app.use(routers.deviceApi);
app.use('/.well-known', express.static('.well-known'));
app.use('/cabana', express.static('cabana/'));

View File

@ -28,17 +28,13 @@ async function signIn(email, password) {
let account = await Accounts.findOne({ where: { email } });
if (!account || !account.dataValues) {
return { success: false, msg: 'BAD ACCOUNT', badAccount: true };
return { success: false, msg: 'BAD ACCOUNT' };
}
account = account.dataValues;
const inputPassword = crypto.createHash('sha256').update(password + process.env.APP_SALT).digest('hex');
if (account.password !== inputPassword) {
return {
success: false,
msg: 'BAD PASSWORD',
invalidPassword: true,
};
return { success: false, msg: 'BAD PASSWORD' };
}
const token = jsonwebtoken.sign({ accountId: account.id }, process.env.APP_SALT);
@ -78,7 +74,7 @@ async function getAuthenticatedAccount(req) {
return getAccountFromJWT(sessionJWT);
}
async function getAccountFromJWT(jwt, limitData) {
async function getAccountFromJWT(jwt, limitData = true) {
let token;
try {
@ -93,7 +89,11 @@ async function getAccountFromJWT(jwt, limitData) {
let query = { where: { id: token.accountId } };
if (limitData) {
query = { ...query, attributes: { exclude: ['password', '2fa_token', 'session_seed'] } };
// we don't want to include sensitive info in the response
query = {
...query,
attributes: { exclude: ['password', '2fa_token', 'session_seed'] },
};
}
const account = await Accounts.findOne(query);

View File

@ -1,9 +1,15 @@
import sanitizeFactory from 'sanitize';
import crypto from 'crypto';
import dirTree from 'directory-tree';
import log4js from 'log4js';
import sanitizeFactory from 'sanitize';
import { Op } from 'sequelize';
import { Devices } from '../../models';
import {
Accounts,
Devices,
Drives,
DriveSegments,
} from '../../models';
import { readJWT, validateJWT } from './authentication';
import { getAccountFromId } from './users';
@ -127,7 +133,7 @@ async function updateDevice(dongleId, data) {
}
async function setIgnoredUploads(dongleId, isIgnored) {
await orm.models.accounts.update(
await Accounts.update(
{ dongle_id: dongleId },
{ where: { uploads_ignored: isIgnored } },
);
@ -191,19 +197,19 @@ async function getDrives(dongleId, includeDeleted, includeMeta) {
query = { ...query, attributes: { exclude: ['metadata'] } };
}
return orm.models.drives.findAll(query);
return Drives.findAll(query);
}
async function getDrive(identifier) {
const drive = await orm.models.drives.findOne({ where: { identifier } });
logger.info(drive);
if (drive.dataValues) return drive.dataValues;
return null;
const drive = await Drives.findOne({ where: { identifier } });
if (!drive) {
return null;
}
return drive.dataValues;
}
async function getDriveFromidentifier(dongleId, identifier) {
return orm.models.drives.findOne({ where: { dongle_id: dongleId, identifier } });
async function getDriveFromIdentifier(dongleId, identifier) {
return Drives.findOne({ where: { dongle_id: dongleId, identifier } });
}
/*
@ -272,18 +278,15 @@ async function getBootlogs(dongleId) {
async function updateOrCreateDrive(dongleId, identifier, data) {
logger.info('updateOrCreate Drive', dongleId, identifier, data);
const check = await orm.models.drives.findOne({ where: { dongle_id: dongleId, identifier } });
const check = await Drives.findOne({ where: { dongle_id: dongleId, identifier } });
logger.info('checking for existing drive....', check);
if (check) {
return orm.models.drives.update(
data,
{ where: { dongle_id: dongleId, identifier } },
);
return Drives.update(data, { where: { dongle_id: dongleId, identifier } });
}
return orm.models.drives.create({
return Drives.create({
...data,
dongle_id: dongleId,
identifier,
@ -292,18 +295,18 @@ async function updateOrCreateDrive(dongleId, identifier, data) {
async function updateOrCreateDriveSegment(dongleId, identifier, segmentId, data) {
logger.info('updateOrCreate Drive_Segment', dongleId, identifier, data);
const check = await orm.models.drive_segments.findOne({
const check = await DriveSegments.findOne({
where: { segment_id: segmentId, dongle_id: dongleId, drive_identifier: identifier },
});
if (check) {
return orm.models.drive_segments.update(
return DriveSegments.update(
data,
{ where: { segment_id: segmentId, dongle_id: dongleId, drive_identifier: identifier } },
);
}
return orm.models.drive_segments.create({
return DriveSegments.create({
...data,
segment_id: segmentId,
drive_identifier: identifier,
@ -312,8 +315,7 @@ async function updateOrCreateDriveSegment(dongleId, identifier, segmentId, data)
}
async function getDriveSegment(driveName, segment) {
return orm.models.drive_segments.findOne({
return DriveSegments.findOne({
where: {
segment_id: segment,
drive_identifier: driveName,
@ -356,7 +358,7 @@ export default {
getDrive,
getBootlogs,
getCrashlogs,
getDriveFromidentifier,
getDriveFromIdentifier,
updateOrCreateDrive,
updateOrCreateDriveSegment,
getDriveSegment,

View File

@ -0,0 +1,18 @@
import authenticationController from '../controllers/authentication';
export const isAuthenticated = async (req, res, next) => {
const account = await authenticationController.getAuthenticatedAccount(req);
if (!account) {
res.status(401).json({
success: false,
code: 'NOT_AUTHENTICATED',
});
return;
}
req.account = account;
next();
};
export default null;

View File

@ -19,6 +19,8 @@ function runAsyncWrapper(callback) {
};
}
// TODO(cameron): clean up this mess into separate files
// DRIVE & BOOT/CRASH LOG FILE UPLOAD HANDLING
router.put('/backend/post_upload', bodyParser.raw({
@ -266,7 +268,7 @@ async function upload(req, res) {
responseUrl = `${process.env.BASE_UPLOAD_URL}?file=${filename}&dir=${directory}&dongleId=${dongleId}&ts=${ts}&token=${token}`;
logger.info(`HTTP.UPLOAD_URL matched 'drive' file upload, constructed responseUrl: ${responseUrl}`);
const drive = await deviceController.getDriveFromidentifier(dongleId, driveName).catch((err)=>{
const drive = await deviceController.getDriveFromIdentifier(dongleId, driveName).catch((err)=>{
logger.warn("drive failed to make", err)
})

View File

@ -0,0 +1,50 @@
import bodyParser from 'body-parser';
import express from 'express';
import authenticationController from '../../controllers/authentication';
import { isAuthenticated } from '../../middlewares/authentication';
const router = express.Router();
router.get('/session', isAuthenticated, async (req, res) => {
return res.status(200).json({
success: true,
data: {
user: req.account.dataValues,
},
});
});
router.post('/login', bodyParser.urlencoded({ extended: true }), async (req, res) => {
const signIn = await authenticationController.signIn(req.body.email, req.body.password);
if (!signIn.success) {
return res.status(401).json(signIn);
}
const account = await authenticationController.getAccountFromJWT(signIn.jwt, true);
return res.status(200).cookie('jwt', signIn.jwt).json({
success: true,
data: {
jwt: signIn.jwt,
user: account.dataValues,
},
});
});
router.get('/logout', async (req, res) => {
res.clearCookie('session');
return res.json({ success: true });
});
// router.get('/session/get', async (req, res) => {
// const account = await authenticationController.getAuthenticatedAccount(req);
//
// if (!account) {
// res.json({ success: true, hasSession: false, session: {} });
// } else {
// res.json({ success: true, hasSession: false, session: account });
// }
// });
export default router;

View File

@ -2,27 +2,11 @@ import express from 'express';
import log4js from 'log4js';
import { getURL, getToken } from '../../../controllers/authentication/oauth/google';
import authenticationController from '../../../controllers/authentication';
import { isAuthenticated } from '../../../middlewares/authentication';
const router = express.Router();
const logger = log4js.getLogger('default');
async function isAuthenticated(req, res, next) {
const account = await authenticationController.getAuthenticatedAccount(req);
if (account === null) {
res.json({
success: true,
data: {
authenticated: false,
},
});
} else {
req.account = account;
next();
}
}
router.get('/authentication/oauth/callback', async (req, res) => {
logger.info(req.query);
res.json(await getToken(req.query.code, req.query.scope));

View File

@ -0,0 +1,11 @@
import express from 'express';
import { isAuthenticated } from '../../../middlewares/authentication';
const router = express.Router();
router.get('/authentication/twofactor/enrol', isAuthenticated, async (req, res) => {
// TODO: implementation
});
export default router;

View File

@ -4,30 +4,16 @@ import dirTree from 'directory-tree';
import express from 'express';
import log4js from 'log4js';
import authenticationController from '../../controllers/authentication';
import { isAuthenticated } from '../../middlewares/authentication';
import deviceController from '../../controllers/devices';
import { MutateDevice } from '../../schema/routes/devices';
const logger = log4js.getLogger('default');
// /api/devices
const router = express.Router();
async function isAuthenticated(req, res, next) {
const account = await authenticationController.getAuthenticatedAccount(req);
if (account === null) {
res.json({ success: false, msg: 'NOT_AUTHENTICATED1' });
} else {
req.account = account;
next();
}
}
router.get('/retropilot/0/devices', isAuthenticated, async (req, res) => {
if (!req.account) {
return res.json({ success: false, msg: 'NOT_AUTHENTICATED' });
}
router.get('/', isAuthenticated, async (req, res) => {
const dongles = await deviceController.getDevices(req.account.id);
return res.json({ success: true, data: dongles });
@ -48,26 +34,19 @@ router.get('/retropilot/0/devices', isAuthenticated, async (req, res) => {
}
*/
router.put('/retropilot/0/device/:dongle_id/', [isAuthenticated, bodyParser.json()], async (req, res) => {
if (!req.account) {
return res.json({ success: false, msg: 'NOT_AUTHENTICATED' });
}
router.put('/:dongle_id/', [isAuthenticated, bodyParser.json()], async (req, res) => {
const { body } = req;
logger.info(MutateDevice.isValid(body));
// TODO: response?
return res.json({ success: true });
});
router.get('/retropilot/0/device/:dongle_id/drives/:drive_identifier/segment', isAuthenticated, async (req, res) => {
if (!req.account) {
return res.json({ success: false, msg: 'NOT_AUTHENTICATED' });
}
router.get('/:dongle_id/drives/:drive_identifier/segment', isAuthenticated, async (req, res) => {
const dongleId = req.params.dongle_id;
const accountId = req.account.id;
const isUserAuthorised = await deviceController.isUserAuthorised(dongleId, accountId);
// TODO reduce data returned`
// TODO reduce data returned
if (isUserAuthorised.success === false || isUserAuthorised.data.authorised === false) {
return res.json({ success: false, msg: isUserAuthorised.msg });
}
@ -79,15 +58,12 @@ router.get('/retropilot/0/device/:dongle_id/drives/:drive_identifier/segment', i
return res.json({ success: true, msg: 'ok', data: directoryTree });
});
router.get('/retropilot/0/device/:dongle_id/drives/:deleted', isAuthenticated, async (req, res) => {
if (!req.account) {
return res.json({ success: false, msg: 'NOT_AUTHENTICATED' });
}
router.get('/:dongle_id/drives/:deleted', isAuthenticated, async (req, res) => {
const dongleId = req.params.dongle_id;
const accountId = req.account.id;
const isUserAuthorised = await deviceController.isUserAuthorised(dongleId, accountId);
// TODO reduce data returned`
// TODO reduce data returned
if (isUserAuthorised.success === false || isUserAuthorised.data.authorised === false) {
return res.json({ success: false, msg: isUserAuthorised.msg });
}
@ -97,14 +73,11 @@ router.get('/retropilot/0/device/:dongle_id/drives/:deleted', isAuthenticated, a
return res.json({ success: true, data: dongles });
});
router.get('/retropilot/0/device/:dongle_id/bootlogs', isAuthenticated, async (req, res) => {
if (!req.account) {
return res.json({ success: false, msg: 'NOT_AUTHENTICATED' });
}
router.get('/:dongle_id/bootlogs', isAuthenticated, async (req, res) => {
const dongleId = req.params.dongle_id;
const accountId = req.account.id;
const isUserAuthorised = await deviceController.isUserAuthorised(dongleId, accountId);
// TODO reduce data returned`
// TODO reduce data returned
if (isUserAuthorised.success === false || isUserAuthorised.data.authorised === false) {
return res.json({ success: false, msg: isUserAuthorised.msg });
}
@ -114,14 +87,11 @@ router.get('/retropilot/0/device/:dongle_id/bootlogs', isAuthenticated, async (r
return res.json({ success: true, data: bootlogs });
});
router.get('/retropilot/0/device/:dongle_id/crashlogs', isAuthenticated, async (req, res) => {
if (!req.account) {
return res.json({ success: false, msg: 'NOT_AUTHENTICATED' });
}
router.get('/:dongle_id/crashlogs', isAuthenticated, async (req, res) => {
const dongleId = req.params.dongle_id;
const accountId = req.account.id;
const isUserAuthorised = await deviceController.isUserAuthorised(dongleId, accountId);
// TODO reduce data returned`
// TODO reduce data returned
if (isUserAuthorised.success === false || isUserAuthorised.data.authorised === false) {
return res.json({ success: false, msg: isUserAuthorised.msg });
}

View File

@ -0,0 +1,14 @@
import express from 'express';
import auth from './auth';
import devices from './devices';
import realtime from './realtime';
const router = express.Router();
// /api
router.use('/auth', auth);
router.use('/devices', devices);
router.use('/realtime', realtime);
export default router;

View File

@ -3,7 +3,9 @@ import express from 'express';
import { AthenaReturnedData } from '../../../models';
import authenticationController from '../../controllers/authentication';
import deviceController from '../../controllers/devices';
import { isAuthenticated } from '../../middlewares/authentication';
// /api/realtime
const router = express.Router();
const whitelistParams = {
@ -24,17 +26,11 @@ const whitelistParams = {
takesnapshot: true,
};
router.get('/dongle/:dongle_id/connected', async (req, res) => {
const account = await authenticationController.getAuthenticatedAccount(req);
if (account == null) {
return res.status(403).json({
error: true,
errorMsg: 'Unauthenticated',
errorObject: { authenticated: false },
});
}
// TODO: use middleware to get device from dongle id
router.get('/:dongleId/connected', isAuthenticated, async (req, res) => {
const { account, params: { dongleId } } = req;
const { dongle_id: dongleId } = req.params;
const device = await deviceController.getDeviceFromDongle(dongleId);
if (!device) {
return res.status(400).json({
@ -64,8 +60,10 @@ router.get('/dongle/:dongle_id/connected', async (req, res) => {
});
});
router.get('/dongle/:dongle_id/send/:method/', async (req, res) => {
const { method } = req.params;
// TODO: change to POST request
router.get('/:dongleId/send/:method/', isAuthenticated, async (req, res) => {
const { account, params: { dongleId, method } } = req;
if (!whitelistParams[method.toLowerCase()]) {
return res.status(409).json({
error: true,
@ -74,16 +72,6 @@ router.get('/dongle/:dongle_id/send/:method/', async (req, res) => {
});
}
const account = await authenticationController.getAuthenticatedAccount(req);
if (account == null) {
return res.status(403).json({
error: true,
errorMsg: 'Unauthenticated',
errorObject: { authenticated: false },
});
}
const { dongle_id: dongleId } = req.params;
const device = await deviceController.getDeviceFromDongle(dongleId);
if (!device) {
return res.status(400).json({
@ -113,7 +101,7 @@ router.get('/dongle/:dongle_id/send/:method/', async (req, res) => {
});
});
router.get('/dongle/:dongle_id/get', async (req, res) => {
router.get('/:dongle_id/get', async (req, res) => {
const account = await authenticationController.getAuthenticatedAccount(req);
if (account == null) {
return res.status(403).json({
@ -150,7 +138,8 @@ router.get('/dongle/:dongle_id/get', async (req, res) => {
}));
});
router.get('/dongle/:dongle_id/temp/nav/:lat/:long', async (req, res) => {
// TODO: change to POST request
router.get('/:dongle_id/temp/nav/:lat/:long', async (req, res) => {
if (!req.params.lat || !req.params.long) {
return res.status(403).json({ error: true, errorMsg: 'Malformed_Request', errorObject: { malformed: true } });
}

View File

@ -6,7 +6,7 @@ import useradminapi from './userAdminApi';
import admin from './administration/adminApi';
import realtime from './api/realtime';
import deviceApi from './api/devices';
import authenticationApi from './api/authentication';
import authenticationApi from './api/auth';
import oauthAuthenticator from './api/authentication/oauth';
export default {

View File

@ -26,7 +26,7 @@ function runAsyncWrapper(callback) {
};
}
if(process.env.NODE_ENV === 'development') {
if (process.env.NODE_ENV === 'development') {
router.get('/useradmin/createbaseaccount', runAsyncWrapper(async (req, res) => {
res.send(await userController.createBaseAccount());
}));
@ -609,8 +609,8 @@ router.get('/useradmin/drive/:dongleId/:driveIdentifier', runAsyncWrapper(async
if (directoryTree.children[i].children[c].name == 'rlog.bz2') rlog = '' + driveUrl + segment + '<a target="_blank" href="/">' + directoryTree.children[i].children[c].name + '' + directoryTree.children[i].children[c].name + '</a>';
}
var isProcessed = '?';
var isStalled = '?';
let isProcessed = '?';
let isStalled = '?';
const drive_segment = await models.__db.get('SELECT * FROM drive_segments WHERE segment_id = ? AND drive_identifier = ? AND dongle_id = ?', parseInt(segment), drive.identifier, device.dongle_id);

View File

@ -1,84 +0,0 @@
import bodyParser from 'body-parser';
import express from 'express';
import authenticationController from '../../controllers/authentication';
const router = express.Router();
async function isAuthenticated(req, res, next) {
const account = await authenticationController.getAuthenticatedAccount(req);
if (account === null) {
res.json({
success: true,
data: {
authenticated: false,
},
});
} else {
req.account = account;
next();
}
}
router.get('/retropilot/0/useradmin/session', isAuthenticated, async (req, res) => {
const account = await authenticationController.getAccountFromJWT(req.cookies.jwt, true);
if (account) {
return res.json({
success: true,
data: {
authenticated: true,
user: account.dataValues,
},
});
}
return res.json({
success: true,
data: {
authenticated: false,
},
});
});
router.post('/retropilot/0/useradmin/auth', bodyParser.urlencoded({ extended: true }), async (req, res) => {
const signIn = await authenticationController.signIn(req.body.email, req.body.password);
if (signIn.success) {
res.cookie('jwt', signIn.jwt);
const account = await authenticationController.getAccountFromJWT(signIn.jwt, true);
res.json({
success: true,
data: {
authenticated: true,
jwt: signIn.jwt,
user: account.dataValues,
},
});
} else {
res.json({
success: true,
data: {
authenticated: false,
},
});
}
});
router.get('/retropilot/0/useradmin/signout', async (req, res) => {
res.clearCookie('session');
return res.json({ success: true });
});
router.get('/session/get', async (req, res) => {
const account = await authenticationController.getAuthenticatedAccount(req);
if (!account) {
res.json({ success: true, hasSession: false, session: {} });
} else {
res.json({ success: true, hasSession: false, session: account });
}
});
export default router;

View File

@ -1,28 +0,0 @@
import express from 'express';
import jsonwebtoken from 'jsonwebtoken';
import { getURL, getToken } from '../../../controllers/authentication/oauth/google';
import authenticationController from '../../../controllers/authentication';
const router = express.Router();
async function isAuthenticated(req, res, next) {
const account = await authenticationController.getAuthenticatedAccount(req);
if (account === null) {
res.json({
success: true,
data: {
authenticated: false,
},
});
} else {
req.account = account;
next();
}
}
router.get('/authentication/twofactor/enrol', isAuthenticated, async (req, res) => {
// TODO: implementation
});
export default router;