diff --git a/libs/email.js b/libs/email.js index a7a352471..6b61d9186 100644 --- a/libs/email.js +++ b/libs/email.js @@ -750,11 +750,11 @@ module.exports = function (app) { var token = jwt.sign( { paths: [ - 'POST_/api/topics/:topicId/comments/:commentId/reports/:reportId/moderate' + 'POST /api/topics/:topicId/comments/:commentId/reports/:reportId/moderate' .replace(':topicId', commentInfo.topic.id) .replace(':commentId', commentInfo.comment.id) .replace(':reportId', report.id), - 'GET_/api/topics/:topicId/comments/:commentId/reports/:reportId' + 'GET /api/topics/:topicId/comments/:commentId/reports/:reportId' .replace(':topicId', commentInfo.topic.id) .replace(':commentId', commentInfo.comment.id) .replace(':reportId', report.id) diff --git a/libs/middleware/authTokenRistrictedUse.js b/libs/middleware/authTokenRistrictedUse.js new file mode 100644 index 000000000..8d5724c37 --- /dev/null +++ b/libs/middleware/authTokenRistrictedUse.js @@ -0,0 +1,66 @@ +'use strict'; + +/** + * Middleware to parse authorization token that is issued for limited use. + * Such tokens are issued for example to Moderators to moderate a Topic or a Comment (argument) + * + * The token is read from: + * * Authorization header + * * URL parameter "token" + * + * Every new presence overrides previous one. So URL parameter "token" overrides "Authorization" header. + * + * @param {object} req Express request object + * @param {object} res Express response object + * @param {function} next Express middleware function + * + * @returns {void} + */ +module.exports = function (req, res, next) { + var app = req.app; + var config = app.get('config'); + var logger = app.get('logger'); + var jwt = app.get('jwt'); + + var token; + + if (req.headers && req.headers.authorization) { + var headerInfoArr = req.headers.authorization.split(' '); // "Bearer " + + if (headerInfoArr[0] === 'Bearer') { + token = headerInfoArr[1]; + } + } + + if (req.query && req.query.token) { + token = req.query.token; + } + + if (!token) { + return res.unauthorised('Missing authorization token. Specify it in authorization header or as a "token" query parameter'); + } + + jwt.verify(token, config.session.publicKey, {algorithms: [config.session.algorithm]}, function (err, eventTokenData) { + if (err) { + if (err.name === 'TokenExpiredError') { + logger.info('loginCheck - JWT token has expired', req.method, req.path, err); + + return res.unauthorised('JWT token has expired'); + } else { + logger.warn('loginCheck - JWT error', req.method, req.path, req.headers, err); + + return res.unauthorised('Invalid JWT token'); + } + } + + // FIXME: Implement - https://github.com/citizenos/citizenos-api/issues/70 + if (!eventTokenData || !eventTokenData.paths || eventTokenData.paths.indexOf(req.method + ' ' + req.path) < 0) { + logger.warn('Invalid token used to access path', req.method + '_' + req.path, '. Token was issued for path', eventTokenData.paths); + + return res.unauthorised('Invalid JWT token'); + } + + return next(); + }); + +}; diff --git a/routes/api/topic.js b/routes/api/topic.js index 6f88e8f11..a68d8bf75 100644 --- a/routes/api/topic.js +++ b/routes/api/topic.js @@ -3450,6 +3450,69 @@ module.exports = function (app) { app.get('/api/users/:userId/topics/:topicId/attachments', loginCheck(['partner']), hasPermission(TopicMemberUser.LEVELS.read, true), topicAttachmentsList); app.get('/api/topics/:topicId/attachments', hasVisibility(Topic.VISIBILITY.public), topicAttachmentsList); + var topicReportsCreate = function (req, res, next) { + var topicId = req.params.topicId; + + db + .transaction(function (t) { + return Report + .create( + { + type: req.body.type, + text: req.body.text, + creatorId: req.user.id, + creatorIp: req.ip + }, + { + transaction: t + } + ) + .then(function (report) { + // FIXME: Topic report create activity! + return TopicReport + .create( + { + topicId: topicId, + reportId: report.id + }, + { + transaction: t + } + ) + .then(function () { + return report; + }); + }); + }) + .then(function (report) { + //FIXME: Send TopicReport e-mail - emailLib.sendCommentReport(commentId, report); // Fire and forget + + return res.ok(report); + }) + .catch(next); + }; + + app.post(['/api/users/:userId/topics/:topicId/reports', '/api/topics/:topicId/reports'], loginCheck(['partner']), topicReportsCreate); + + /** + * Read Topic Report + */ + app.get(['/api/topics/:topicId/reports/:reportId', '/api/users/:userId/topics/:topicId/reports/:reportId'], function (req, res, next) { + //FIXME Implement + return res.notImplemented(); + }); + + /** + * Moderate a Topic + */ + app.post('/api/topics/:topicId/comments/:commentId/reports/:reportId/moderate', function (req, res, next) { + var reportType = req.body.type; // Delete reason type which is provided in case deleted/hidden by moderator due to a user report + var reportText = req.body.text; // Free text with reason why the comment was deleted/hidden + + // FIXME: Implement + return res.notImplemented(); + }); + /** * Create Topic Comment */ @@ -3995,58 +4058,12 @@ module.exports = function (app) { .catch(next); }; - var topicReportsCreate = function (req, res, next) { - var topicId = req.params.topicId; - - db - .transaction(function (t) { - return Report - .create( - { - type: req.body.type, - text: req.body.text, - creatorId: req.user.id, - creatorIp: req.ip - }, - { - transaction: t - } - ) - .then(function (report) { - // FIXME: Topic report create activity! - return TopicReport - .create( - { - topicId: topicId, - reportId: report.id - }, - { - transaction: t - } - ) - .then(function () { - return report; - }); - }); - }) - .then(function (report) { - //FIXME: Send TopicReport e-mail - emailLib.sendCommentReport(commentId, report); // Fire and forget - - return res.ok(report); - }) - .catch(next); - }; - - app.post(['/api/users/:userId/topics/:topicId/reports', '/api/topics/:topicId/reports'], loginCheck(['partner']), topicReportsCreate); - - /** * Read (List) Topic Comments */ app.get('/api/users/:userId/topics/:topicId/comments', loginCheck(['partner']), hasPermission(TopicMemberUser.LEVELS.read, true), isModerator(), topicCommentsList); app.get('/api/v2/users/:userId/topics/:topicId/comments', loginCheck(['partner']), hasPermission(TopicMemberUser.LEVELS.read, true), isModerator(), topicCommentsList2); - /** * Read (List) public Topic Comments */ @@ -4254,7 +4271,7 @@ module.exports = function (app) { /** - * Read Report + * Read Comment (Argument) report */ app.get(['/api/topics/:topicId/comments/:commentId/reports/:reportId', '/api/users/:userId/topics/:topicId/comments/:commentId/reports/:reportId'], function (req, res, next) { if (!req.headers || !req.headers.authorization) { @@ -6718,42 +6735,42 @@ module.exports = function (app) { var userId = req.user.id; var topicId = req.params.topicId; - return db.transaction(function (t) { - return TopicFavourite - .findOrCreate({ - where: { - topicId: topicId, - userId: userId - }, - transaction: t - }) - .spread(function (topicFavourite, created) { - if (created) { - return Topic - .findOne({ - where: { - id: topicId - } - }) - .then(function (topic) { - topic.description = null; + db.transaction(function (t) { + return TopicFavourite + .findOrCreate({ + where: { + topicId: topicId, + userId: userId + }, + transaction: t + }) + .spread(function (topicFavourite, created) { + if (created) { + return Topic + .findOne({ + where: { + id: topicId + } + }) + .then(function (topic) { + topic.description = null; - return cosActivities - .addActivity( - topic, - { - type: 'User', - id: userId - }, - null, - topicFavourite, - req.method + ' ' + req.path, - t - ); - }); - } - }); - }) + return cosActivities + .addActivity( + topic, + { + type: 'User', + id: userId + }, + null, + topicFavourite, + req.method + ' ' + req.path, + t + ); + }); + } + }); + }) .then(function () { return res.ok(); }) @@ -6786,8 +6803,8 @@ module.exports = function (app) { return cosActivities .deleteActivity( - topicFavourite, - topic, + topicFavourite, + topic, { type: 'User', id: req.user.id diff --git a/test/api/topic.js b/test/api/topic.js index 8f22a3538..14416062f 100644 --- a/test/api/topic.js +++ b/test/api/topic.js @@ -9377,7 +9377,7 @@ suite('Topics', function () { var token = jwt.sign( { paths: [ - 'POST_/api/topics/:topicId/comments/:commentId/reports/:reportId/moderate' + 'POST /api/topics/:topicId/comments/:commentId/reports/:reportId/moderate' .replace(':topicId', topic.id) .replace(':commentId', comment.id) .replace(':reportId', report.id) @@ -9466,7 +9466,7 @@ suite('Topics', function () { var token = jwt.sign( { paths: [ - 'POST_/api/topics/:topicId/comments/:commentId/reports/:reportId/moderate' + 'POST /api/topics/:topicId/comments/:commentId/reports/:reportId/moderate' .replace(':topicId', topic.id) .replace(':commentId', comment.id) .replace(':reportId', report.id)