diff --git a/api/middleware/permission-check.js b/api/middleware/permission-check.js index 0fc499f..f232858 100644 --- a/api/middleware/permission-check.js +++ b/api/middleware/permission-check.js @@ -5,7 +5,6 @@ let check = (requiredPermission, actualPermission, res, next) => { if (actualPermission >= requiredPermission) { return next(); } - return res.status(403).send({ success: false, message: 'permission denied' diff --git a/api/models/app-user.js b/api/models/app-user.js index d410f2f..09083d3 100644 --- a/api/models/app-user.js +++ b/api/models/app-user.js @@ -20,6 +20,14 @@ const AppUserSchema = new Schema({ min: 0, max: 4, default: 0 + }, + secret: { + type: String, + required: true + }, + activated: { + type: Boolean, + default: false } }, { collection: 'app_user', diff --git a/api/routes/authenticate.js b/api/routes/authenticate.js index 3c56f66..dd05c6e 100644 --- a/api/routes/authenticate.js +++ b/api/routes/authenticate.js @@ -8,6 +8,9 @@ const Q = require('q'); const _ = require('lodash'); const logger = require('debug')('cc:authenticate'); +const apiAuthenticationMiddleware = require('../middleware/auth-middleware'); +const checkAdmin = require('../middleware/permission-check').checkAdmin; + // HTTP status codes by name const codes = require('./http-codes'); @@ -49,11 +52,12 @@ let authCheck = (username, password) => { const diff = 3 * 60 * 24; // time till expiration [minutes] - if (user && bcrypt.compareSync(password, user.password)) { + if (user && user.activated && bcrypt.compareSync(password, user.password)) { // authentication successful deferred.resolve({ _id: user._id, username: user.username, + permission: user.permission, token: jwt.sign({sub: user._id}, config.secret, {expiresIn: diff * 60}), tokenExpireDate: new Date(Date.now().valueOf() + diff * 60000 - 1000) }); @@ -67,57 +71,94 @@ let authCheck = (username, password) => { }; -//******************************** SIGNUP ************************ -// -// authenticate.route('/signup') -// .post((req, res, next) => { -// create(req.body) -// .then(() => { -// res.sendStatus(200); -// }) -// .catch((err) => { -// res.status(400).send(err); -// }); -// }) -// -// .all( -// routerHandling.httpMethodNotAllowed -// ); -// -// let create = (userParam) => { -// const deferred = Q.defer(); -// -// // validation -// AppUserModel.findOne( -// {username: userParam.username}, -// (err, user) => { -// if (err) deferred.reject(err.name + ': ' + err.message); -// -// if (user) { -// // username already exists -// deferred.reject('Username "' + userParam.username + '" is already taken'); -// } else { -// createUser(); -// } -// }); -// -// let createUser = () => { -// // set user object to userParam without the cleartext password -// const user = _.omit(userParam, 'password'); -// -// // add hashed password to user object -// user.password = bcrypt.hashSync(userParam.password, 10); -// -// const newUser = new AppUserModel(user); -// newUser.save((err, doc) => { -// if (err) deferred.reject(err.name + ': ' + err.message); -// -// deferred.resolve(); -// }); -// }; -// -// return deferred.promise; -// }; +// ******************************** EDITING USING ADMIN PANEL ************************ + +authenticate.route('/editUser/:id') + .patch(apiAuthenticationMiddleware, checkAdmin, (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); + err.status = codes.notfound; + next(err); + return; // prevent node to process this function further after next() has finished. + } + + // increment version manually as we do not use .save(.) + req.body.updatedAt = new Date(); + req.body.$inc = {__v: 1}; + + // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing. + AppUserModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("appUser not found"); + err.status = codes.notfound; + } + else { + res.locals.items = item; + } + next(err); + }) + }) + + .all( + routerHandling.httpMethodNotAllowed + ); + + +// ******************************** SIGNUP ************************ + +authenticate.route('/signup') + .post((req, res, next) => { + create(req.body) + .then(() => { + res.sendStatus(200); + }) + .catch((err) => { + res.status(400).send(err); + }); + }) + + .all( + routerHandling.httpMethodNotAllowed + ); + +let create = (userParam) => { + const deferred = Q.defer(); + + // validation + AppUserModel.findOne( + {username: userParam.username}, + (err, user) => { + if (err) deferred.reject(err.name + ': ' + err.message); + + if (user) { + // username already exists + deferred.reject('Username "' + userParam.username + '" is already taken'); + } else { + createUser(); + } + }); + + let createUser = () => { + // set user object to userParam without the cleartext password + const user = _.omit(userParam, 'password'); + + // add hashed password to user object + user.password = bcrypt.hashSync(userParam.password, 10); + + const newUser = new AppUserModel(user); + newUser.save((err, doc) => { + if (err) deferred.reject(err.name + ': ' + err.message); + + deferred.resolve(); + }); + }; + + return deferred.promise; +}; authenticate.use(routerHandling.emptyResponse);