Regression: Reactivate direct conversations only if all involved users are active (#21714)

pull/21839/head^2
Kevin Aleman 4 years ago committed by GitHub
parent aff6653f6f
commit b655a8fd3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 23
      app/lib/server/functions/setUserActiveStatus.js
  2. 12
      app/models/server/models/Rooms.js
  3. 9
      app/models/server/models/Users.js
  4. 252
      tests/end-to-end/api/24-methods.js

@ -9,6 +9,25 @@ import { relinquishRoomOwnerships } from './relinquishRoomOwnerships';
import { shouldRemoveOrChangeOwner, getSubscribedRoomsForUserWithDetails } from './getRoomsWithSingleOwner';
import { getUserSingleOwnedRooms } from './getUserSingleOwnedRooms';
function reactivateDirectConversations(userId) {
// since both users can be deactivated at the same time, we should just reactivate rooms if both users are active
// for that, we need to fetch the direct messages, fetch the users involved and then the ids of rooms we can reactivate
const directConversations = Rooms.getDirectConversationsByUserId(userId, { projection: { _id: 1, uids: 1 } }).fetch();
const userIds = directConversations.reduce((acc, r) => acc.push(...r.uids) && acc, []);
const uniqueUserIds = [...new Set(userIds)];
const activeUsers = Users.findActiveByUserIds(uniqueUserIds, { projection: { _id: 1 } }).fetch();
const activeUserIds = activeUsers.map((u) => u._id);
const roomsToReactivate = directConversations.reduce((acc, room) => {
const otherUserId = room.uids.find((u) => u !== userId);
if (activeUserIds.includes(otherUserId)) {
acc.push(room._id);
}
return acc;
}, []);
Rooms.setDmReadOnlyByUserId(userId, roomsToReactivate, false, false);
}
export function setUserActiveStatus(userId, active, confirmRelinquish = false) {
check(userId, String);
check(active, Boolean);
@ -39,10 +58,10 @@ export function setUserActiveStatus(userId, active, confirmRelinquish = false) {
if (active === false) {
Users.unsetLoginTokens(userId);
Rooms.setReadOnlyByUserId(userId, true, false);
Rooms.setDmReadOnlyByUserId(userId, undefined, true, false);
} else {
Users.unsetReason(userId);
Rooms.setReadOnlyByUserId(userId, false, false);
reactivateDirectConversations(userId);
}
if (active && !settings.get('Accounts_Send_Email_When_Activating')) {
return true;

@ -229,9 +229,13 @@ export class Rooms extends Base {
return this.update(query, update);
}
setReadOnlyByUserId(_id, readOnly, reactWhenReadOnly) {
setDmReadOnlyByUserId(_id, ids, readOnly, reactWhenReadOnly) {
const query = {
uids: _id,
uids: {
$size: 2,
$in: [_id],
},
...ids && Array.isArray(ids) ? { _id: { $in: ids } } : {},
t: 'd',
};
@ -245,6 +249,10 @@ export class Rooms extends Base {
return this.update(query, update, { multi: true });
}
getDirectConversationsByUserId(_id, options) {
return this.find({ t: 'd', uids: { $size: 2, $in: [_id] } }, options);
}
setAllowReactingWhenReadOnlyById = function(_id, allowReacting) {
const query = {
_id,

@ -735,6 +735,15 @@ export class Users extends Base {
}, options);
}
findActiveByUserIds(ids, options = {}) {
return this.find({
active: true,
type: { $nin: ['app'] },
roles: { $ne: ['guest'] },
_id: { $in: ids },
}, options);
}
findActiveLocalGuests(idExceptions = [], options = {}) {
const query = {
active: true,

@ -1479,4 +1479,256 @@ describe('Meteor.methods', function() {
});
});
});
describe('[@setUserActiveStatus]', () => {
let testUser;
let testUser2;
const testUserCredentials = {};
let dmId;
let dmTestId;
before('create test user', (done) => {
const username = `user.test.${ Date.now() }`;
const email = `${ username }@rocket.chat`;
request.post(api('users.create'))
.set(credentials)
.send({ email, name: username, username, password: username, roles: ['user'] })
.end((err, res) => {
testUser = res.body.user;
done();
});
});
before('create test user 2', (done) => {
const username = `user.test.${ Date.now() }`;
const email = `${ username }@rocket.chat`;
request.post(api('users.create'))
.set(credentials)
.send({ email, name: username, username, password: username, roles: ['user'] })
.end((err, res) => {
testUser2 = res.body.user;
done();
});
});
before('login testUser', (done) => {
request.post(api('login'))
.send({
user: testUser.username,
password: testUser.username,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
testUserCredentials['X-Auth-Token'] = res.body.data.authToken;
testUserCredentials['X-User-Id'] = res.body.data.userId;
})
.end(done);
});
before('create direct conversation with user', (done) => {
request.post(methodCall('createDirectMessage'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'createDirectMessage',
params: [testUser.username],
}),
})
.end((err, res) => {
const result = JSON.parse(res.body.message);
expect(result.result).to.be.an('object');
expect(result.result).to.have.property('rid').that.is.an('string');
dmId = result.result.rid;
done();
});
});
before('create direct conversation between both users', (done) => {
request.post(methodCall('createDirectMessage'))
.set(testUserCredentials)
.send({
message: JSON.stringify({
method: 'createDirectMessage',
params: [testUser2.username],
}),
})
.end((err, res) => {
const result = JSON.parse(res.body.message);
expect(result.result).to.be.an('object');
expect(result.result).to.have.property('rid').that.is.an('string');
dmTestId = result.result.rid;
done();
});
});
it('should deactivate a user', (done) => {
request.post(methodCall('setUserActiveStatus'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'setUserActiveStatus',
params: [testUser._id, false, false],
}),
})
.end((err, res) => {
expect(res.body).to.have.property('success').that.is.an('boolean');
const result = JSON.parse(res.body.message);
expect(result.result).to.be.equal(true);
done();
});
});
it('should deactivate another user', (done) => {
request.post(methodCall('setUserActiveStatus'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'setUserActiveStatus',
params: [testUser2._id, false, false],
}),
})
.end((err, res) => {
expect(res.body).to.have.property('success').that.is.an('boolean');
const result = JSON.parse(res.body.message);
expect(result.result).to.be.equal(true);
done();
});
});
it('should mark the direct conversation between admin=>testUser as readonly when user is deactivated', (done) => {
request.post(methodCall('getRoomByTypeAndName'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'getRoomByTypeAndName',
params: ['d', dmId],
}),
})
.end((err, res) => {
expect(res.body.success).to.equal(true);
const result = JSON.parse(res.body.message);
expect(result.result.ro).to.equal(true);
done();
});
});
it('should activate a user', (done) => {
request.post(methodCall('setUserActiveStatus'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'setUserActiveStatus',
params: [testUser._id, true, false],
}),
})
.end((err, res) => {
expect(res.body).to.have.property('success').that.is.an('boolean');
const result = JSON.parse(res.body.message);
expect(result.result).to.be.equal(true);
done();
});
});
it('should set readonly=false when user is activated (and the other side is also active)', (done) => {
request.post(methodCall('getRoomByTypeAndName'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'getRoomByTypeAndName',
params: ['d', dmId],
}),
})
.end((err, res) => {
expect(res.body.success).to.equal(true);
const result = JSON.parse(res.body.message);
expect(result.result.ro).to.equal(false);
done();
});
});
it('should keep the direct conversation between testUser=>testUser2 as readonly when one of them is deactivated', (done) => {
request.post(api('login'))
.send({
user: testUser.username,
password: testUser.username,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
testUserCredentials['X-Auth-Token'] = res.body.data.authToken;
testUserCredentials['X-User-Id'] = res.body.data.userId;
})
.then(() => {
request.post(methodCall('getRoomByTypeAndName'))
.set(testUserCredentials)
.send({
message: JSON.stringify({
method: 'getRoomByTypeAndName',
params: ['d', dmTestId],
}),
})
.end((err, res) => {
expect(res.body.success).to.equal(true);
const result = JSON.parse(res.body.message);
expect(result.result.ro).to.equal(true);
done();
});
})
.catch(done);
});
it('should activate another user', (done) => {
request.post(methodCall('setUserActiveStatus'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'setUserActiveStatus',
params: [testUser2._id, true, false],
}),
})
.end((err, res) => {
expect(res.body).to.have.property('success').that.is.an('boolean');
const result = JSON.parse(res.body.message);
expect(result.result).to.be.equal(true);
done();
});
});
it('should set readonly=false when both users are activated', (done) => {
request.post(methodCall('getRoomByTypeAndName'))
.set(testUserCredentials)
.send({
message: JSON.stringify({
method: 'getRoomByTypeAndName',
params: ['d', dmTestId],
}),
})
.end((err, res) => {
expect(res.body.success).to.equal(true);
const result = JSON.parse(res.body.message);
expect(result.result.ro).to.equal(false);
done();
});
});
it('should keep readonly=true when user is activated (and the other side is deactivated)', (done) => {
request.post(methodCall('getRoomByTypeAndName'))
.set(testUserCredentials)
.send({
message: JSON.stringify({
method: 'getRoomByTypeAndName',
params: ['d', dmTestId],
}),
})
.end((err, res) => {
expect(res.body.success).to.equal(true);
const result = JSON.parse(res.body.message);
expect(result.result.ro).to.equal(false);
done();
});
});
});
});

Loading…
Cancel
Save