diff --git a/app/api/server/lib/messages.js b/app/api/server/lib/messages.js index 7ef9e8f5212ed..9fdd48b33e5bf 100644 --- a/app/api/server/lib/messages.js +++ b/app/api/server/lib/messages.js @@ -58,11 +58,42 @@ export async function findStarredMessages({ uid, roomId, pagination: { offset, c }; } +export async function findSnippetedMessageById({ uid, messageId }) { + if (!await getValue('Message_AllowSnippeting')) { + throw new Error('error-not-allowed'); + } + + if (!uid) { + throw new Error('invalid-user'); + } + + const snippet = await Messages.findOne({ _id: messageId, snippeted: true }); + + if (!snippet) { + throw new Error('invalid-message'); + } + + const room = await Rooms.findOneById(snippet.rid); + + if (!room) { + throw new Error('invalid-message'); + } + + if (!await canAccessRoomAsync(room, { _id: uid })) { + throw new Error('error-not-allowed'); + } + + return { + message: snippet, + }; +} + export async function findSnippetedMessages({ uid, roomId, pagination: { offset, count, sort } }) { if (!await getValue('Message_AllowSnippeting')) { throw new Error('error-not-allowed'); } const room = await Rooms.findOneById(roomId); + if (!await canAccessRoomAsync(room, { _id: uid })) { throw new Error('error-not-allowed'); } diff --git a/app/api/server/v1/chat.js b/app/api/server/v1/chat.js index e5dd3e28941aa..2d0b0d58cbff6 100644 --- a/app/api/server/v1/chat.js +++ b/app/api/server/v1/chat.js @@ -9,7 +9,7 @@ import { API } from '../api'; import Rooms from '../../../models/server/models/Rooms'; import Users from '../../../models/server/models/Users'; import { settings } from '../../../settings'; -import { findMentionedMessages, findStarredMessages, findSnippetedMessages } from '../lib/messages'; +import { findMentionedMessages, findStarredMessages, findSnippetedMessageById, findSnippetedMessages } from '../lib/messages'; API.v1.addRoute('chat.delete', { authRequired: true }, { post() { @@ -644,6 +644,20 @@ API.v1.addRoute('chat.getStarredMessages', { authRequired: true }, { }, }); +API.v1.addRoute('chat.getSnippetedMessageById', { authRequired: true }, { + get() { + const { messageId } = this.queryParams; + + if (!messageId) { + throw new Meteor.Error('error-invalid-params', 'The required "messageId" query param is missing.'); + } + const message = Promise.await(findSnippetedMessageById({ + uid: this.userId, + messageId, + })); + return API.v1.success(message); + }, +}); API.v1.addRoute('chat.getSnippetedMessages', { authRequired: true }, { get() { diff --git a/app/message-snippet/client/page/snippetPage.js b/app/message-snippet/client/page/snippetPage.js index 208b8e5b38cf2..c4bd5d0b4a69f 100644 --- a/app/message-snippet/client/page/snippetPage.js +++ b/app/message-snippet/client/page/snippetPage.js @@ -1,19 +1,19 @@ -import { Meteor } from 'meteor/meteor'; import { FlowRouter } from 'meteor/kadira:flow-router'; +import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; import moment from 'moment'; import { DateFormat } from '../../../lib'; import { settings } from '../../../settings'; import { Markdown } from '../../../markdown/client'; -import { SnippetedMessages } from '../lib/collections'; +import { APIClient } from '../../../utils/client'; Template.snippetPage.helpers({ snippet() { - return SnippetedMessages.findOne({ _id: FlowRouter.getParam('snippetId') }); + return Template.instance().message.get(); }, snippetContent() { - const message = SnippetedMessages.findOne({ _id: FlowRouter.getParam('snippetId') }); + const message = Template.instance().message.get(); if (message === undefined) { return null; } @@ -22,22 +22,23 @@ Template.snippetPage.helpers({ return markdown.tokens[0].text; }, date() { - const snippet = SnippetedMessages.findOne({ _id: FlowRouter.getParam('snippetId') }); + const snippet = Template.instance().message.get(); if (snippet !== undefined) { return moment(snippet.ts).format(settings.get('Message_DateFormat')); } }, time() { - const snippet = SnippetedMessages.findOne({ _id: FlowRouter.getParam('snippetId') }); + const snippet = Template.instance().message.get(); if (snippet !== undefined) { return DateFormat.formatTime(snippet.ts); } }, }); -Template.snippetPage.onCreated(function() { +Template.snippetPage.onCreated(async function() { const snippetId = FlowRouter.getParam('snippetId'); - this.autorun(function() { - Meteor.subscribe('snippetedMessage', snippetId); - }); + this.message = new ReactiveVar({}); + + const { message } = await APIClient.v1.get(`chat.getSnippetedMessageById?messageId=${ snippetId }`); + this.message.set(message); }); diff --git a/app/message-snippet/server/publications/snippetedMessage.js b/app/message-snippet/server/publications/snippetedMessage.js index 968d7e84401ed..2d4722ce15dac 100644 --- a/app/message-snippet/server/publications/snippetedMessage.js +++ b/app/message-snippet/server/publications/snippetedMessage.js @@ -3,6 +3,7 @@ import { Meteor } from 'meteor/meteor'; import { Messages, Users, Rooms } from '../../../models'; Meteor.publish('snippetedMessage', function(_id) { + console.warn('The publication "snippetedMessage" is deprecated and will be removed after version v3.0.0'); if (typeof this.userId === 'undefined' || this.userId === null) { return this.ready(); } diff --git a/tests/end-to-end/api/05-chat.js b/tests/end-to-end/api/05-chat.js index 5874d8d8c0d0e..e4d515768f2eb 100644 --- a/tests/end-to-end/api/05-chat.js +++ b/tests/end-to-end/api/05-chat.js @@ -2159,6 +2159,35 @@ describe('Threads', () => { }); }); + describe('[/chat.getSnippetedMessageById]', () => { + it('should return an error when the snippeted messages is disabled', (done) => { + updateSetting('Message_AllowSnippeting', false).then(() => { + request.get(api('chat.getSnippetedMessageById?messageId=invalid-id')) + .set(credentials) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body.error).to.be.equal('error-not-allowed'); + }) + .end(done); + }); + }); + it('should return an error when the required "messageId" parameter is not sent', (done) => { + updateSetting('Message_AllowSnippeting', true).then(() => { + request.get(api('chat.getSnippetedMessageById')) + .set(credentials) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body.errorType).to.be.equal('error-invalid-params'); + }) + .end(done); + }); + }); + }); + describe('[/chat.getSnippetedMessages]', () => { it('should return an error when the required "roomId" parameter is not sent', (done) => { request.get(api('chat.getSnippetedMessages')) @@ -2214,5 +2243,17 @@ describe('Threads', () => { .end(done); }); }); + + it('should return an error when the messageId is invalid', (done) => { + request.get(api('chat.getSnippetedMessageById?messageId=invalid-id')) + .set(credentials) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body.error).to.be.equal('invalid-message'); + }) + .end(done); + }); }); });