From 9b8bcd0722cbe2c67b127da0d17c1f312cb51b14 Mon Sep 17 00:00:00 2001 From: Florian Hartwich Date: Thu, 8 Jun 2017 13:14:53 +0200 Subject: [PATCH] Add permission level checks --- api/middleware/auth-middleware.js | 12 +++++++++++- api/middleware/permission-check.js | 20 ++++++++++++++++++++ api/middleware/util.js | 2 +- api/models/app-user.js | 2 +- api/routes/decorations.js | 7 ++++--- api/routes/ranks.js | 7 ++++--- api/routes/squads.js | 7 ++++--- api/routes/users.js | 9 +++++---- api/server.js | 15 +++++---------- api/test/users.spec.js | 3 ++- 10 files changed, 57 insertions(+), 27 deletions(-) create mode 100644 api/middleware/permission-check.js diff --git a/api/middleware/auth-middleware.js b/api/middleware/auth-middleware.js index aecac3e..06b78db 100644 --- a/api/middleware/auth-middleware.js +++ b/api/middleware/auth-middleware.js @@ -2,6 +2,7 @@ const jwt = require('jsonwebtoken'); const config = require('../config/config'); +const AppUser = require('../models/app-user'); const apiAuthentication = (req, res, next) => { @@ -18,7 +19,16 @@ const apiAuthentication = (req, res, next) => { } else { // if everything is good, save to request for use in other routes req.decoded = decoded; - next(); + AppUser.findById(decoded.sub, (err, item) => { + if (err) { + return res.status(403).send({ + success: false, + message: 'token is not associated to any actual user' + }); + } + req.user = item; + next(); + }); } }); diff --git a/api/middleware/permission-check.js b/api/middleware/permission-check.js new file mode 100644 index 0000000..0fc499f --- /dev/null +++ b/api/middleware/permission-check.js @@ -0,0 +1,20 @@ +"use strict"; + + +let check = (requiredPermission, actualPermission, res, next) => { + if (actualPermission >= requiredPermission) { + return next(); + } + + return res.status(403).send({ + success: false, + message: 'permission denied' + }); +}; + +module.exports = { + checkSql: (req, res, next) => { check(1, req.user.permission, res, next) }, + checkHl: (req, res, next) => { check(2, req.user.permission, res, next) }, + checkMT: (req, res, next) => { check(3, req.user.permission, res, next) }, + checkAdmin: (req, res, next) => { check(4, req.user.permission, res, next) } +}; diff --git a/api/middleware/util.js b/api/middleware/util.js index 15085b6..1ab04d2 100644 --- a/api/middleware/util.js +++ b/api/middleware/util.js @@ -1,4 +1,4 @@ -"use strict" +"use strict"; const sortCollectionBy = (collection, key) => { collection.sort((a, b) => { diff --git a/api/models/app-user.js b/api/models/app-user.js index 5fd30b7..d410f2f 100644 --- a/api/models/app-user.js +++ b/api/models/app-user.js @@ -18,7 +18,7 @@ const AppUserSchema = new Schema({ get: v => Math.round(v), set: v => Math.round(v), min: 0, - max: 3, + max: 4, default: 0 } }, { diff --git a/api/routes/decorations.js b/api/routes/decorations.js index f2c8743..3a6daab 100644 --- a/api/routes/decorations.js +++ b/api/routes/decorations.js @@ -12,6 +12,7 @@ const logger = require('debug')('cc:decorations'); const codes = require('./http-codes'); const apiAuthenticationMiddleware = require('../middleware/auth-middleware'); +const checkHl = require('../middleware/permission-check').checkHl; const routerHandling = require('../middleware/router-handling'); // Mongoose Model using mongoDB @@ -46,7 +47,7 @@ decoration.route('/') }); }) - .post(apiAuthenticationMiddleware, upload.single('image'), (req, res, next) => { + .post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { const decoration = new DecorationModel(req.body); // timestamp and default are set automatically by Mongoose Schema Validation decoration.save((err) => { @@ -86,7 +87,7 @@ decoration.route('/:id') }); }) - .patch(apiAuthenticationMiddleware, upload.single('image'), (req, res, next) => { + .patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { if (!req.body || (req.body._id && req.body._id !== req.params.id)) { // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); @@ -125,7 +126,7 @@ decoration.route('/:id') }) }) - .delete(apiAuthenticationMiddleware, (req, res, next) => { + .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { DecorationModel.findByIdAndRemove(req.params.id, (err, item) => { if (err) { err.status = codes.wrongrequest; diff --git a/api/routes/ranks.js b/api/routes/ranks.js index 2eb9a25..c80e788 100644 --- a/api/routes/ranks.js +++ b/api/routes/ranks.js @@ -12,6 +12,7 @@ const logger = require('debug')('cc:ranks'); const codes = require('./http-codes'); const apiAuthenticationMiddleware = require('../middleware/auth-middleware'); +const checkHl = require('../middleware/permission-check').checkHl; const routerHandling = require('../middleware/router-handling'); // Mongoose Model using mongoDB @@ -49,7 +50,7 @@ ranks.route('/') }); }) - .post(apiAuthenticationMiddleware, upload.single('image'), (req, res, next) => { + .post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { const rank = new RankModel(req.body); // timestamp and default are set automatically by Mongoose Schema Validation rank.save((err) => { @@ -89,7 +90,7 @@ ranks.route('/:id') }); }) - .patch(apiAuthenticationMiddleware, upload.single('image'), (req, res, next) => { + .patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { if (!req.body || (req.body._id && req.body._id !== req.params.id)) { // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match @@ -130,7 +131,7 @@ ranks.route('/:id') }) }) - .delete(apiAuthenticationMiddleware, (req, res, next) => { + .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { RankModel.findByIdAndRemove(req.params.id, (err, item) => { if (err) { err.status = codes.wrongrequest; diff --git a/api/routes/squads.js b/api/routes/squads.js index 63ba679..e9d5bf4 100644 --- a/api/routes/squads.js +++ b/api/routes/squads.js @@ -12,6 +12,7 @@ const logger = require('debug')('cc:squads'); const codes = require('./http-codes'); const apiAuthenticationMiddleware = require('../middleware/auth-middleware'); +const checkHl = require('../middleware/permission-check').checkHl; const routerHandling = require('../middleware/router-handling'); // Mongoose Model using mongoDB @@ -44,7 +45,7 @@ squads.route('/') }); }) - .post(apiAuthenticationMiddleware, upload.single('image'), (req, res, next) => { + .post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { const squad = new SquadModel(req.body); // timestamp and default are set automatically by Mongoose Schema Validation if (req.file) { @@ -89,7 +90,7 @@ squads.route('/:id') }); }) - .patch(apiAuthenticationMiddleware, upload.single('image'), (req, res, next) => { + .patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { if (!req.body || (req.body._id && req.body._id !== req.params.id)) { // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match @@ -129,7 +130,7 @@ squads.route('/:id') }) }) - .delete(apiAuthenticationMiddleware, (req, res, next) => { + .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { SquadModel.findByIdAndRemove(req.params.id, (err, item) => { if (err) { err.status = codes.wrongrequest; diff --git a/api/routes/users.js b/api/routes/users.js index f883e4b..1af4034 100644 --- a/api/routes/users.js +++ b/api/routes/users.js @@ -9,6 +9,7 @@ const logger = require('debug')('me2u5:users'); const codes = require('./http-codes'); const apiAuthenticationMiddleware = require('../middleware/auth-middleware'); +const checkHl = require('../middleware/permission-check').checkHl; const sortCollectionBy = require('../middleware/util').sortCollection; const routerHandling = require('../middleware/router-handling'); @@ -84,7 +85,7 @@ users.route('/') } }) - .post(apiAuthenticationMiddleware, (req, res, next) => { + .post(apiAuthenticationMiddleware, checkHl, (req, res, next) => { const user = new UserModel(req.body); // timestamp and default are set automatically by Mongoose Schema Validation user.save((err) => { @@ -129,7 +130,7 @@ users.route('/:id') }); }) - .patch(apiAuthenticationMiddleware, (req, res, next) => { + .patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => { if (!req.body || (req.body._id && req.body._id !== req.params.id)) { // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); @@ -176,7 +177,7 @@ users.route('/:id') }) }) - .put(apiAuthenticationMiddleware, (req, res, next) => { + .put(apiAuthenticationMiddleware, checkHl, (req, res, next) => { // first check that the given element id is the same as the URL id if (!req.body || req.body._id !== req.params.id) { // the URL does not fit the given element @@ -231,7 +232,7 @@ users.route('/:id') }) }) - .delete(apiAuthenticationMiddleware, (req, res, next) => { + .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { UserModel.findByIdAndRemove(req.params.id, (err, item) => { if (err) { err.status = codes.wrongrequest; diff --git a/api/server.js b/api/server.js index 9f289b0..e60f222 100644 --- a/api/server.js +++ b/api/server.js @@ -15,6 +15,8 @@ const urls = require('./config/api-url'); const restAPIchecks = require('./middleware/request-checks.js'); const errorResponseWare = require('./middleware/error-response'); const apiAuthenticationMiddleware = require('./middleware/auth-middleware'); +const checkHl = require('./middleware/permission-check').checkHl; +const checkAdmin = require('./middleware/permission-check').checkAdmin; const signatureCronJob = require('./cron-job/update-signatures'); // router modules @@ -54,7 +56,7 @@ app.use(restAPIchecks); // Routes ****************************************************** app.use(urls.signatures, signatureRouter); -// initialize logging here to exclude /signature requests +// initialize logging at this point to exclude /signature requests if (process.env.NODE_ENV === config.dev.env) { // development logging app.use(requestLogger('dev')); @@ -69,8 +71,8 @@ app.use(urls.users, userRouter); app.use(urls.squads, squadRouter); app.use(urls.ranks, rankRouter); app.use(urls.decorations, decorationRouter); -app.use(urls.awards, apiAuthenticationMiddleware, awardingRouter); -app.use(urls.command, apiAuthenticationMiddleware, commandRouter); +app.use(urls.awards, apiAuthenticationMiddleware, checkHl, awardingRouter); +app.use(urls.command, apiAuthenticationMiddleware, checkAdmin, commandRouter); // send index.html on all different paths app.use(function (req, res) { @@ -105,13 +107,6 @@ if (process.env.NODE_ENV !== config.test.env) { }); } else { mongoose.connect(config.database.uri + config.test.db); - for (let collection in mongoose.connection.collections) { - mongoose.connection.collections[collection].drop(function (err) { - if (err) { - console.log('Error while test-Db clean up: ' + err); - } - }); - } app.listen(config.test.port); console.log('Listening on port ' + config.test.port); } diff --git a/api/test/users.spec.js b/api/test/users.spec.js index 75e887e..cc67ad8 100644 --- a/api/test/users.spec.js +++ b/api/test/users.spec.js @@ -51,7 +51,8 @@ describe('Users', () => { before(function (done) { let appUser = { username: 'testUsr', - password: '$2a$10$i9cBC06uGJnnrqQCh8COkuZLMChLQqw5j4K0yfDQn1udTDAompHka' + password: '$2a$10$i9cBC06uGJnnrqQCh8COkuZLMChLQqw5j4K0yfDQn1udTDAompHka', + permission: 2 }; let appUserEncoded = { username: appUser.username,