diff --git a/app/api/server/v1/roles.js b/app/api/server/v1/roles.js index e66e5495987..c17e43107c6 100644 --- a/app/api/server/v1/roles.js +++ b/app/api/server/v1/roles.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; -import { Roles } from '../../../models'; +import { Roles, Users } from '../../../models'; import { API } from '../api'; import { getUsersInRole, hasPermission } from '../../../authorization/server'; import { settings } from '../../../settings/server/index'; @@ -156,7 +156,7 @@ API.v1.addRoute('roles.update', { authRequired: true }, { if (roleData.name) { const otherRole = Roles.findOneByIdOrName(roleData.name); - if (otherRole && otherRole._id !== role.roleId) { + if (otherRole && otherRole._id !== role._id) { throw new Meteor.Error('error-duplicate-role-names-not-allowed', 'Role name already exists'); } } @@ -213,3 +213,62 @@ API.v1.addRoute('roles.delete', { authRequired: true }, { return API.v1.success(); }, }); + +API.v1.addRoute('roles.removeUserFromRole', { authRequired: true }, { + post() { + check(this.bodyParams, { + roleName: String, + username: String, + }); + + const data = { + roleName: this.bodyParams.roleName, + username: this.bodyParams.username, + }; + + if (!hasPermission(this.userId, 'access-permissions')) { + throw new Meteor.Error('error-not-allowed', 'Accessing permissions is not allowed'); + } + + const user = Users.findOneByUsername(data.username); + + if (!user) { + throw new Meteor.Error('error-invalid-user', 'There is no user with this username'); + } + + const role = Roles.findOneByIdOrName(data.roleName); + + if (!role) { + throw new Meteor.Error('error-invalid-roleId', 'This role does not exist'); + } + + if (user.roles.indexOf(role.name) === -1) { + throw new Meteor.Error('error-user-not-in-role', 'User is not in this role'); + } + + if (role._id === 'admin') { + const adminCount = Roles.findUsersInRole('admin', role.scope).count(); + if (adminCount === 1) { + throw new Meteor.Error('error-admin-required', 'You need to have at least one admin'); + } + } + + Roles.removeUserRoles(user._id, role.name, role.scope); + + if (settings.get('UI_DisplayRoles')) { + api.broadcast('user.roleUpdate', { + type: 'removed', + _id: role._id, + u: { + _id: user._id, + username: user.username, + }, + scope: role.scope, + }); + } + + return API.v1.success({ + role, + }); + }, +}); diff --git a/tests/end-to-end/api/13-roles.js b/tests/end-to-end/api/13-roles.js index 207aa83b229..f1ca6ea0591 100644 --- a/tests/end-to-end/api/13-roles.js +++ b/tests/end-to-end/api/13-roles.js @@ -31,19 +31,18 @@ function createRole(name, scope, description) { }); } -function addUserToRole(roleId, username) { +function addUserToRole(roleName, username) { return new Promise((resolve) => { request.post(api('roles.addUserToRole')) .set(credentials) .send({ - roleName: roleId, + roleName, username, }) .expect('Content-Type', 'application/json') .expect(200) .expect((res) => { expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('role._id', roleId); }) .end((err, req) => { resolve(req.body.role); @@ -385,4 +384,55 @@ describe('[Roles]', function() { .end(done); }); }); + + describe('POST [/roles.removeUserFromRole]', () => { + let usersScopedRole; + let subscriptionsScopedRole; + + before(async () => { + usersScopedRole = await createRole(`usersScopedRole-${ Date.now() }`, 'Users'); + subscriptionsScopedRole = await createRole(`subscriptionsScopedRole-${ Date.now() }`, 'Subscriptions'); + + await addUserToRole(usersScopedRole.name, login.user); + await addUserToRole(subscriptionsScopedRole.name, login.user); + }); + + it('should unassign a role with User scope from an user', (done) => { + request.post(api('roles.removeUserFromRole')) + .set(credentials) + .send({ + roleName: usersScopedRole.name, + username: login.user, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('role._id', usersScopedRole._id); + expect(res.body).to.have.nested.property('role.name', usersScopedRole.name); + expect(res.body).to.have.nested.property('role.scope', usersScopedRole.scope); + expect(res.body).to.have.nested.property('role.description', usersScopedRole.description); + }) + .end(done); + }); + + it('should unassign a role with Subscriptions scope from an user', (done) => { + request.post(api('roles.removeUserFromRole')) + .set(credentials) + .send({ + roleName: subscriptionsScopedRole.name, + username: login.user, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('role._id', subscriptionsScopedRole._id); + expect(res.body).to.have.nested.property('role.name', subscriptionsScopedRole.name); + expect(res.body).to.have.nested.property('role.scope', subscriptionsScopedRole.scope); + expect(res.body).to.have.nested.property('role.description', subscriptionsScopedRole.description); + }) + .end(done); + }); + }); });