From e3f167921c28fa5ad40bb6f140caceb92006c84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Thu, 11 Nov 2021 16:01:56 +0100 Subject: [PATCH] Added auth middleware. Added access control to apps --- api.js | 1 + controllers/apps/createApp.js | 5 +++++ controllers/apps/deleteApp.js | 5 +++++ controllers/apps/getAllApps.js | 5 +++++ controllers/apps/getSingleApp.js | 5 ++++- controllers/apps/reorderApps.js | 5 +++++ controllers/apps/updateApp.js | 5 +++++ controllers/auth/index.js | 1 + controllers/auth/validate.js | 21 +++++++++++++++++++++ db/migrations/02_resource-access.js | 2 +- middleware/auth.js | 25 +++++++++++++++++++++++++ models/App.js | 2 +- models/Bookmark.js | 2 +- models/Category.js | 2 +- routes/apps.js | 11 ++++++++--- routes/auth.js | 4 +++- 16 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 controllers/auth/validate.js create mode 100644 middleware/auth.js diff --git a/api.js b/api.js index 9eb9b9f..9caa377 100644 --- a/api.js +++ b/api.js @@ -21,6 +21,7 @@ api.use('/api/weather', require('./routes/weather')); api.use('/api/categories', require('./routes/category')); api.use('/api/bookmarks', require('./routes/bookmark')); api.use('/api/queries', require('./routes/queries')); +api.use('/api/auth', require('./routes/auth')); // Custom error handler api.use(errorHandler); diff --git a/controllers/apps/createApp.js b/controllers/apps/createApp.js index 361e77e..67951a7 100644 --- a/controllers/apps/createApp.js +++ b/controllers/apps/createApp.js @@ -1,11 +1,16 @@ const asyncWrapper = require('../../middleware/asyncWrapper'); const App = require('../../models/App'); const loadConfig = require('../../utils/loadConfig'); +const ErrorResponse = require('../../utils/ErrorResponse'); // @desc Create new app // @route POST /api/apps // @access Public const createApp = asyncWrapper(async (req, res, next) => { + if (!req.isAuthenticated) { + return next(new ErrorResponse('Unauthorized', 401)); + } + const { pinAppsByDefault } = await loadConfig(); let app; diff --git a/controllers/apps/deleteApp.js b/controllers/apps/deleteApp.js index ed55729..5c9f77d 100644 --- a/controllers/apps/deleteApp.js +++ b/controllers/apps/deleteApp.js @@ -1,10 +1,15 @@ const asyncWrapper = require('../../middleware/asyncWrapper'); const App = require('../../models/App'); +const ErrorResponse = require('../../utils/ErrorResponse'); // @desc Delete app // @route DELETE /api/apps/:id // @access Public const deleteApp = asyncWrapper(async (req, res, next) => { + if (!req.isAuthenticated) { + return next(new ErrorResponse('Unauthorized', 401)); + } + await App.destroy({ where: { id: req.params.id }, }); diff --git a/controllers/apps/getAllApps.js b/controllers/apps/getAllApps.js index 1172e34..04a7585 100644 --- a/controllers/apps/getAllApps.js +++ b/controllers/apps/getAllApps.js @@ -25,13 +25,18 @@ const getAllApps = asyncWrapper(async (req, res, next) => { await useKubernetes(apps); } + // apps visibility + const where = req.isAuthenticated ? {} : { isPublic: true }; + if (orderType == 'name') { apps = await App.findAll({ order: [[Sequelize.fn('lower', Sequelize.col('name')), 'ASC']], + where, }); } else { apps = await App.findAll({ order: [[orderType, 'ASC']], + where, }); } diff --git a/controllers/apps/getSingleApp.js b/controllers/apps/getSingleApp.js index 9a06b68..ce19464 100644 --- a/controllers/apps/getSingleApp.js +++ b/controllers/apps/getSingleApp.js @@ -1,12 +1,15 @@ const asyncWrapper = require('../../middleware/asyncWrapper'); const App = require('../../models/App'); +const ErrorResponse = require('../../utils/ErrorResponse'); // @desc Get single app // @route GET /api/apps/:id // @access Public const getSingleApp = asyncWrapper(async (req, res, next) => { + const visibility = req.isAuthenticated ? {} : { isPublic: true }; + const app = await App.findOne({ - where: { id: req.params.id }, + where: { id: req.params.id, ...visibility }, }); if (!app) { diff --git a/controllers/apps/reorderApps.js b/controllers/apps/reorderApps.js index 29794b3..24e102d 100644 --- a/controllers/apps/reorderApps.js +++ b/controllers/apps/reorderApps.js @@ -1,10 +1,15 @@ const asyncWrapper = require('../../middleware/asyncWrapper'); const App = require('../../models/App'); +const ErrorResponse = require('../../utils/ErrorResponse'); // @desc Reorder apps // @route PUT /api/apps/0/reorder // @access Public const reorderApps = asyncWrapper(async (req, res, next) => { + if (!req.isAuthenticated) { + return next(new ErrorResponse('Unauthorized', 401)); + } + req.body.apps.forEach(async ({ id, orderId }) => { await App.update( { orderId }, diff --git a/controllers/apps/updateApp.js b/controllers/apps/updateApp.js index 2a996fb..af2ab50 100644 --- a/controllers/apps/updateApp.js +++ b/controllers/apps/updateApp.js @@ -1,10 +1,15 @@ const asyncWrapper = require('../../middleware/asyncWrapper'); const App = require('../../models/App'); +const ErrorResponse = require('../../utils/ErrorResponse'); // @desc Update app // @route PUT /api/apps/:id // @access Public const updateApp = asyncWrapper(async (req, res, next) => { + if (!req.isAuthenticated) { + return next(new ErrorResponse('Unauthorized', 401)); + } + let app = await App.findOne({ where: { id: req.params.id }, }); diff --git a/controllers/auth/index.js b/controllers/auth/index.js index 8341f7c..d8198fa 100644 --- a/controllers/auth/index.js +++ b/controllers/auth/index.js @@ -1,3 +1,4 @@ module.exports = { login: require('./login'), + validate: require('./validate'), }; diff --git a/controllers/auth/validate.js b/controllers/auth/validate.js new file mode 100644 index 0000000..975f455 --- /dev/null +++ b/controllers/auth/validate.js @@ -0,0 +1,21 @@ +const asyncWrapper = require('../../middleware/asyncWrapper'); +const ErrorResponse = require('../../utils/ErrorResponse'); +const jwt = require('jsonwebtoken'); + +// @desc Verify token +// @route POST /api/auth/verify +// @access Public +const validate = asyncWrapper(async (req, res, next) => { + try { + jwt.verify(req.body.token, process.env.SECRET); + + res.status(200).json({ + success: true, + data: { token: { isValid: true } }, + }); + } catch (err) { + return next(new ErrorResponse('Token expired', 401)); + } +}); + +module.exports = validate; diff --git a/db/migrations/02_resource-access.js b/db/migrations/02_resource-access.js index 717ac38..78652d5 100644 --- a/db/migrations/02_resource-access.js +++ b/db/migrations/02_resource-access.js @@ -7,7 +7,7 @@ const up = async (query) => { const template = { type: INTEGER, allowNull: true, - defaultValue: 0, + defaultValue: 1, }; for await (let table of tables) { diff --git a/middleware/auth.js b/middleware/auth.js new file mode 100644 index 0000000..c73d0ca --- /dev/null +++ b/middleware/auth.js @@ -0,0 +1,25 @@ +const jwt = require('jsonwebtoken'); + +const auth = (req, res, next) => { + const authHeader = req.header('Authorization'); + let token; + let tokenIsValid = false; + + if (authHeader && authHeader.startsWith('Bearer ')) { + token = authHeader.split(' ')[1]; + } + + if (token) { + try { + jwt.verify(token, process.env.SECRET); + } finally { + tokenIsValid = true; + } + } + + req.isAuthenticated = tokenIsValid; + + next(); +}; + +module.exports = auth; diff --git a/models/App.js b/models/App.js index c71d350..8971d60 100644 --- a/models/App.js +++ b/models/App.js @@ -29,7 +29,7 @@ const App = sequelize.define( isPublic: { type: DataTypes.INTEGER, allowNull: true, - defaultValue: 0, + defaultValue: 1, }, }, { diff --git a/models/Bookmark.js b/models/Bookmark.js index 652c119..159ea28 100644 --- a/models/Bookmark.js +++ b/models/Bookmark.js @@ -23,7 +23,7 @@ const Bookmark = sequelize.define( isPublic: { type: DataTypes.INTEGER, allowNull: true, - defaultValue: 0, + defaultValue: 1, }, }, { diff --git a/models/Category.js b/models/Category.js index 582a6c2..9025613 100644 --- a/models/Category.js +++ b/models/Category.js @@ -20,7 +20,7 @@ const Category = sequelize.define( isPublic: { type: DataTypes.INTEGER, allowNull: true, - defaultValue: 0, + defaultValue: 1, }, }, { diff --git a/routes/apps.js b/routes/apps.js index 6f1e817..7405ebe 100644 --- a/routes/apps.js +++ b/routes/apps.js @@ -1,6 +1,7 @@ const express = require('express'); const router = express.Router(); const upload = require('../middleware/multer'); +const auth = require('../middleware/auth'); const { createApp, @@ -11,10 +12,14 @@ const { reorderApps, } = require('../controllers/apps'); -router.route('/').post(upload, createApp).get(getAllApps); +router.route('/').post(auth, upload, createApp).get(auth, getAllApps); -router.route('/:id').get(getSingleApp).put(upload, updateApp).delete(deleteApp); +router + .route('/:id') + .get(auth, getSingleApp) + .put(auth, upload, updateApp) + .delete(auth, deleteApp); -router.route('/0/reorder').put(reorderApps); +router.route('/0/reorder').put(auth, reorderApps); module.exports = router; diff --git a/routes/auth.js b/routes/auth.js index cfd4771..3208176 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -1,9 +1,11 @@ const express = require('express'); const router = express.Router(); -const { login } = require('../controllers/auth'); +const { login, validate } = require('../controllers/auth'); const requireBody = require('../middleware/requireBody'); router.route('/').post(requireBody(['password', 'duration']), login); +router.route('/validate').post(requireBody(['token']), validate); + module.exports = router;