Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .docker/Dockerfile.rhel
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM registry.access.redhat.com/ubi8/nodejs-12

ENV RC_VERSION 3.11.1
ENV RC_VERSION 3.11.2

MAINTAINER [email protected]

Expand Down
7 changes: 7 additions & 0 deletions .github/history-manual.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,12 @@
"contributors": [
"sampaiodiego"
]
}],
"3.11.2": [{
"title": "[FIX] Security Hotfix (https://docs.rocket.chat/guides/security/security-updates)",
"userLogin": "sampaiodiego",
"contributors": [
"sampaiodiego"
]
}]
}
41 changes: 41 additions & 0 deletions .github/history.json
Original file line number Diff line number Diff line change
Expand Up @@ -55308,6 +55308,47 @@
]
}
]
},
"3.11.2": {
"node_version": "12.18.4",
"npm_version": "6.14.8",
"apps_engine_version": "1.22.2",
"mongo_versions": [
"3.4",
"3.6",
"4.0"
],
"pull_requests": [
{
"pr": "20727",
"title": "[FIX] Room owner not being able to override global retention policy",
"userLogin": "g-thome",
"description": "use correct permissions to check if room owner can override global retention policy",
"milestone": "3.11.2",
"contributors": [
"g-thome"
]
},
{
"pr": "20860",
"title": "[FIX] Prevent Message Attachment rendering",
"userLogin": "ggazzo",
"milestone": "3.11.2",
"contributors": [
"ggazzo"
]
},
{
"pr": "20740",
"title": "[FIX] External systems not being able to change Omnichannel Inquiry priorities ",
"userLogin": "renatobecker",
"description": "Due to a wrong property name, external applications were not able to change the priority of Omnichannel Inquires.",
"milestone": "3.11.2",
"contributors": [
"renatobecker"
]
}
]
}
}
}
2 changes: 1 addition & 1 deletion .snapcraft/resources/prepareRocketChat
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

curl -SLf "https://releases.rocket.chat/3.11.1/download/" -o rocket.chat.tgz
curl -SLf "https://releases.rocket.chat/3.11.2/download/" -o rocket.chat.tgz

tar xf rocket.chat.tgz --strip 1

Expand Down
2 changes: 1 addition & 1 deletion .snapcraft/snap/snapcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# 5. `snapcraft snap`

name: rocketchat-server
version: 3.11.1
version: 3.11.2
summary: Rocket.Chat server
description: Have your own Slack like online chat, built with Meteor. https://rocket.chat/
confinement: strict
Expand Down
356 changes: 203 additions & 153 deletions HISTORY.md

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion app/authorization/server/functions/hasRole.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Roles } from '../../../models/server/raw';

export const hasRoleAsync = async (userId, roleNames, scope) => {
roleNames = [].concat(roleNames);
if (!userId || userId === '') {
return false;
}

return Roles.isUserInRoles(userId, roleNames, scope);
};

Expand Down
8 changes: 4 additions & 4 deletions app/lib/server/functions/setUserAvatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ export const setUserAvatar = function(user, dataURI, contentType, service) {
try {
result = HTTP.get(dataURI, { npmRequestOptions: { encoding: 'binary', rejectUnauthorized: false } });
if (!result) {
console.log(`Not a valid response, from the avatar url: ${ dataURI }`);
throw new Meteor.Error('error-avatar-invalid-url', `Invalid avatar URL: ${ dataURI }`, { function: 'setUserAvatar', url: dataURI });
console.log(`Not a valid response, from the avatar url: ${ encodeURI(dataURI) }`);
throw new Meteor.Error('error-avatar-invalid-url', `Invalid avatar URL: ${ encodeURI(dataURI) }`, { function: 'setUserAvatar', url: dataURI });
}
} catch (error) {
if (!error.response || error.response.statusCode !== 404) {
console.log(`Error while handling the setting of the avatar from a url (${ dataURI }) for ${ user.username }:`, error);
throw new Meteor.Error('error-avatar-url-handling', `Error while handling avatar setting from a URL (${ dataURI }) for ${ user.username }`, { function: 'RocketChat.setUserAvatar', url: dataURI, username: user.username });
console.log(`Error while handling the setting of the avatar from a url (${ encodeURI(dataURI) }) for ${ user.username }:`, error);
throw new Meteor.Error('error-avatar-url-handling', `Error while handling avatar setting from a URL (${ encodeURI(dataURI) }) for ${ user.username }`, { function: 'RocketChat.setUserAvatar', url: dataURI, username: user.username });
}
}

Expand Down
18 changes: 6 additions & 12 deletions app/meteor-accounts-saml/server/lib/SAML.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ export class SAML {
});
}

private static processValidateAction(req: IIncomingMessage, res: ServerResponse, service: IServiceProviderOptions, samlObject: ISAMLAction): void {
private static processValidateAction(req: IIncomingMessage, res: ServerResponse, service: IServiceProviderOptions, _samlObject: ISAMLAction): void {
const serviceProvider = new SAMLServiceProvider(service);
SAMLUtils.relayState = req.body.RelayState;
serviceProvider.validateResponse(req.body.SAMLResponse, (err, profile/* , loggedOut*/) => {
Expand All @@ -390,21 +390,15 @@ export class SAML {
throw new Error('No user data collected from IdP response.');
}

let credentialToken = (profile.inResponseToId && profile.inResponseToId.value) || profile.inResponseToId || profile.InResponseTo || samlObject.credentialToken;
// create a random token to store the login result
// to test an IdP initiated login on localhost, use the following URL (assuming SimpleSAMLPHP on localhost:8080):
// http://localhost:8080/simplesaml/saml2/idp/SSOService.php?spentityid=http://localhost:3000/_saml/metadata/test-sp
const credentialToken = Random.id();

const loginResult = {
profile,
};

if (!credentialToken) {
// If the login was initiated by the IDP, then we don't have a credentialToken as there was no AuthorizeRequest on our side
// so we create a random token now to use the same url to end the login
//
// to test an IdP initiated login on localhost, use the following URL (assuming SimpleSAMLPHP on localhost:8080):
// http://localhost:8080/simplesaml/saml2/idp/SSOService.php?spentityid=http://localhost:3000/_saml/metadata/test-sp
credentialToken = Random.id();
SAMLUtils.log('[SAML] Using random credentialToken: ', credentialToken);
}

this.storeCredential(credentialToken, loginResult);
const url = `${ Meteor.absoluteUrl('home') }?saml_idp_credentialToken=${ credentialToken }`;
res.writeHead(302, {
Expand Down
2 changes: 1 addition & 1 deletion app/meteor-accounts-saml/server/loginHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const makeError = (message: string): Record<string, any> => ({
});

Accounts.registerLoginHandler('saml', function(loginRequest) {
if (!loginRequest.saml || !loginRequest.credentialToken) {
if (!loginRequest.saml || !loginRequest.credentialToken || typeof loginRequest.credentialToken !== 'string') {
return undefined;
}

Expand Down
6 changes: 4 additions & 2 deletions app/models/server/raw/Roles.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ export class RolesRaw extends BaseRaw {
}

async isUserInRoles(userId, roles, scope) {
roles = [].concat(roles);
if (!Array.isArray(roles)) {
roles = [roles];
}

for (let i = 0, total = roles.length; i < total; i++) {
const roleName = roles[i];

// eslint-disable-next-line no-await-in-loop
const role = await this.findOne({ _id: roleName });
const role = await this.findOne({ _id: roleName }, { scope: 1 });
const roleScope = (role && role.scope) || 'Users';
const model = this.models[roleScope];

Expand Down
5 changes: 4 additions & 1 deletion app/ui-utils/client/lib/Layout.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { Tracker } from 'meteor/tracker';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { ReactiveVar } from 'meteor/reactive-var';

export const Layout = new class RocketChatLayout {
constructor() {
this.embedded = new ReactiveVar();
Tracker.autorun(() => {
this.layout = FlowRouter.getQueryParam('layout');
this.embedded.set(this.layout === 'embedded');
});
}

isEmbedded() {
return FlowRouter.getQueryParam('layout') === 'embedded';
return this.embedded.get();
}
}();
10 changes: 5 additions & 5 deletions app/ui-utils/client/lib/messageContext.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Tracker } from 'meteor/tracker';

import { Subscriptions, Rooms, Users } from '../../../models/client';
import { hasPermission } from '../../../authorization/client';
Expand All @@ -17,7 +18,7 @@ const fields = { name: 1, username: 1, 'settings.preferences.showMessageInMainTh
export function messageContext({ rid } = Template.instance()) {
const uid = Meteor.userId();
const user = Users.findOne({ _id: uid }, { fields }) || {};
const instace = Template.instance();
const instance = Template.instance();
const openThread = (e) => {
const { rid, mid, tmid } = e.currentTarget.dataset;
const room = Rooms.findOne({ _id: rid });
Expand All @@ -40,7 +41,7 @@ export function messageContext({ rid } = Template.instance()) {
});
} : (msg, e) => {
const { actionlink } = e.currentTarget.dataset;
actionLinks.run(actionlink, msg._id, instace, (err) => {
actionLinks.run(actionlink, msg._id, instance, (err) => {
if (err) {
handleError(err);
}
Expand All @@ -60,13 +61,12 @@ export function messageContext({ rid } = Template.instance()) {

return {
u: user,
room: Rooms.findOne({ _id: rid }, {
reactive: false,
room: Tracker.nonreactive(() => Rooms.findOne({ _id: rid }, {
fields: {
_updatedAt: 0,
lastMessage: 0,
},
}),
})),
subscription: Subscriptions.findOne({ rid }, {
fields: {
name: 1,
Expand Down
2 changes: 1 addition & 1 deletion app/utils/rocketchat.info
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "3.11.1"
"version": "3.11.2"
}
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ function EditChannel({ room, onClickClose, onClickBack }) {
const canChangeType = getCanChangeType(room, canCreateChannel, canCreateGroup, isAdmin);
const canSetRo = usePermission('set-readonly', room._id);
const canSetReactWhenRo = usePermission('set-react-when-readonly', room._id);
const canEditPrivilegedSetting = usePermission('edit-privileged-setting', room._id);
const canEditRoomRetentionPolicy = usePermission('edit-room-retention-policy', room._id);
const canArchiveOrUnarchive = useAtLeastOnePermission(useMemo(() => ['archive-room', 'unarchive-room'], []));
const canDelete = usePermission(`delete-${ room.t }`);
const canToggleEncryption = usePermission('toggle-room-e2e-encryption', room._id) && (room.encrypted || e2e.isReady());
Expand Down Expand Up @@ -390,7 +390,7 @@ function EditChannel({ room, onClickClose, onClickBack }) {
<Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}>
<Field.Label>{t('RetentionPolicyRoom_OverrideGlobal')}</Field.Label>
<Field.Row>
<ToggleSwitch disabled={!retentionEnabled || !canEditPrivilegedSetting} checked={retentionOverrideGlobal} onChange={handleRetentionOverrideGlobal}/>
<ToggleSwitch disabled={!retentionEnabled || !canEditRoomRetentionPolicy} checked={retentionOverrideGlobal} onChange={handleRetentionOverrideGlobal} />
</Field.Row>
</Box>
</Field>
Expand Down
2 changes: 1 addition & 1 deletion ee/app/livechat-enterprise/server/api/lib/inquiries.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export async function setPriorityToInquiry({ userId, roomId, priority }) {
if (!await hasPermissionAsync(userId, 'manage-livechat-priorities') && !await hasPermissionAsync(userId, 'view-l-room')) {
throw new Error('error-not-authorized');
}
const inquiry = await LivechatInquiry.findOneByRoomId(roomId, { fields: { queued: 1 } });
const inquiry = await LivechatInquiry.findOneByRoomId(roomId, { fields: { status: 1 } });
if (!inquiry || inquiry.status !== 'queued') {
throw new Error('error-invalid-inquiry');
}
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "Rocket.Chat",
"description": "The Ultimate Open Source WebChat Platform",
"version": "3.11.1",
"version": "3.11.2",
"author": {
"name": "Rocket.Chat",
"url": "https://rocket.chat/"
Expand Down
84 changes: 43 additions & 41 deletions server/methods/addAllUserToRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,54 @@ Meteor.methods({
check(rid, String);
check(activeUsersOnly, Boolean);

if (hasRole(this.userId, 'admin') === true) {
const userCount = Users.find().count();
if (userCount > settings.get('API_User_Limit')) {
throw new Meteor.Error('error-user-limit-exceeded', 'User Limit Exceeded', {
method: 'addAllToRoom',
});
}
if (!hasRole(this.userId, 'admin')) {
throw new Meteor.Error(403, 'Access to Method Forbidden', {
method: 'addAllToRoom',
});
}

const room = Rooms.findOneById(rid);
if (room == null) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', {
method: 'addAllToRoom',
});
}
const userFilter = {};
if (activeUsersOnly === true) {
userFilter.active = true;
}

const userFilter = {};
if (activeUsersOnly === true) {
userFilter.active = true;
}
const userCursor = Users.find(userFilter);
const usersCount = userCursor.count();
if (usersCount > settings.get('API_User_Limit')) {
throw new Meteor.Error('error-user-limit-exceeded', 'User Limit Exceeded', {
method: 'addAllToRoom',
});
}

const users = Users.find(userFilter).fetch();
const now = new Date();
users.forEach(function(user) {
const subscription = Subscriptions.findOneByRoomIdAndUserId(rid, user._id);
if (subscription != null) {
return;
}
callbacks.run('beforeJoinRoom', user, room);
Subscriptions.createWithRoomAndUser(room, user, {
ts: now,
open: true,
alert: true,
unread: 1,
userMentions: 1,
groupMentions: 0,
});
Messages.createUserJoinWithRoomIdAndUser(rid, user, {
ts: now,
});
Meteor.defer(function() {});
return callbacks.run('afterJoinRoom', user, room);
const room = Rooms.findOneById(rid);
if (!room) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', {
method: 'addAllToRoom',
});
return true;
}
throw new Meteor.Error(403, 'Access to Method Forbidden', {
method: 'addAllToRoom',

const users = userCursor.fetch();
const now = new Date();
users.forEach(function(user) {
const subscription = Subscriptions.findOneByRoomIdAndUserId(rid, user._id);
if (subscription != null) {
return;
}
callbacks.run('beforeJoinRoom', user, room);
Subscriptions.createWithRoomAndUser(room, user, {
ts: now,
open: true,
alert: true,
unread: 1,
userMentions: 1,
groupMentions: 0,
});
Messages.createUserJoinWithRoomIdAndUser(rid, user, {
ts: now,
});
Meteor.defer(function() {});
return callbacks.run('afterJoinRoom', user, room);
});
return true;
},
});