diff --git a/app/api/server/v1/users.js b/app/api/server/v1/users.js index fa40c867f0d..2a461f92438 100644 --- a/app/api/server/v1/users.js +++ b/app/api/server/v1/users.js @@ -710,3 +710,9 @@ API.v1.addRoute('users.autocomplete', { authRequired: true }, { }))); }, }); + +API.v1.addRoute('users.removeOtherTokens', { authRequired: true }, { + post() { + API.v1.success(Meteor.call('removeOtherTokens')); + }, +}); diff --git a/tests/data/users.helper.js b/tests/data/users.helper.js index 1490c1e69cc..ec2ae445b3b 100644 --- a/tests/data/users.helper.js +++ b/tests/data/users.helper.js @@ -24,6 +24,15 @@ export const login = (username, password) => new Promise((resolve) => { }); }); +export const deleteUser = (user) => new Promise((resolve) => { + request.post(api('users.delete')) + .set(credentials) + .send({ + userId: user._id, + }) + .end(resolve); +}); + export const getUserByUsername = (username) => new Promise((resolve) => { request.get(api(`users.info?username=${ username }`)) .set(credentials) diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index 3ab97cdb5c9..10c835dcaf3 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -17,7 +17,7 @@ import { adminEmail, preferences, password, adminUsername } from '../../data/use import { imgURL } from '../../data/interactions.js'; import { customFieldText, clearCustomFields, setCustomFields } from '../../data/custom-fields.js'; import { updatePermission, updateSetting } from '../../data/permissions.helper'; -import { createUser, login } from '../../data/users.helper.js'; +import { createUser, login, deleteUser } from '../../data/users.helper.js'; describe('[Users]', function() { this.retries(0); @@ -419,42 +419,21 @@ describe('[Users]', function() { describe('[/users.setAvatar]', () => { let user; - before((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 }) - .end((err, res) => { - user = res.body.user; - done(); - }); + before(async () => { + user = await createUser(); }); let userCredentials; - before((done) => { - request.post(api('login')) - .send({ - user: user.username, - password, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - userCredentials = {}; - userCredentials['X-Auth-Token'] = res.body.data.authToken; - userCredentials['X-User-Id'] = res.body.data.userId; - }) - .end(done); + before(async () => { + userCredentials = await login(user.username, password); }); before((done) => { updatePermission('edit-other-user-info', ['admin', 'user']).then(done); }); - after((done) => { - request.post(api('users.delete')).set(credentials).send({ - userId: user._id, - }).end(() => updatePermission('edit-other-user-info', ['admin']).then(done)); + after(async () => { + await deleteUser(user); user = undefined; + await updatePermission('edit-other-user-info', ['admin']); }); it('should set the avatar of the logged user by a local image', (done) => { request.post(api('users.setAvatar')) @@ -1758,4 +1737,49 @@ describe('[Users]', function() { .end(done); }); }); + + describe('[/users.removeOtherTokens]', () => { + let user; + let userCredentials; + let newCredentials; + + before(async () => { + user = await createUser(); + userCredentials = await login(user.username, password); + newCredentials = await login(user.username, password); + }); + after(async () => { + await deleteUser(user); + user = undefined; + }); + + it('should invalidate all active sesions', (done) => { + /* We want to validate that the login with the "old" credentials fails + However, the removal of the tokens is done asynchronously. + Thus, we check that within the next seconds, at least one try to + access an authentication requiring route fails */ + let counter = 0; + + async function checkAuthenticationFails() { + const result = await request.get(api('me')) + .set(userCredentials); + return result.statusCode === 401; + } + + async function tryAuthentication() { + if (await checkAuthenticationFails()) { + done(); + } else if (++counter < 20) { + setTimeout(tryAuthentication, 1000); + } else { + done('Session did not invalidate in time'); + } + } + + request.post(api('users.removeOtherTokens')) + .set(newCredentials) + .expect(200) + .then(tryAuthentication); + }); + }); });