diff --git a/app/api/server/v1/roles.js b/app/api/server/v1/roles.js index 5f2b39f624b..e66e5495987 100644 --- a/app/api/server/v1/roles.js +++ b/app/api/server/v1/roles.js @@ -181,3 +181,35 @@ API.v1.addRoute('roles.update', { authRequired: true }, { }); }, }); + +API.v1.addRoute('roles.delete', { authRequired: true }, { + post() { + check(this.bodyParams, { + roleId: String, + }); + + if (!hasPermission(this.userId, 'access-permissions')) { + throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed'); + } + + const role = Roles.findOneByIdOrName(this.bodyParams.roleId); + + if (!role) { + throw new Meteor.Error('error-invalid-roleId', 'This role does not exist'); + } + + if (role.protected) { + throw new Meteor.Error('error-role-protected', 'Cannot delete a protected role'); + } + + const existingUsers = Roles.findUsersInRole(role._id, role.scope); + + if (existingUsers && existingUsers.count() > 0) { + throw new Meteor.Error('error-role-in-use', 'Cannot delete role because it\'s in use'); + } + + Roles.remove(role._id); + + return API.v1.success(); + }, +}); diff --git a/tests/end-to-end/api/13-roles.js b/tests/end-to-end/api/13-roles.js index a05b5206eea..207aa83b229 100644 --- a/tests/end-to-end/api/13-roles.js +++ b/tests/end-to-end/api/13-roles.js @@ -31,6 +31,26 @@ function createRole(name, scope, description) { }); } +function addUserToRole(roleId, username) { + return new Promise((resolve) => { + request.post(api('roles.addUserToRole')) + .set(credentials) + .send({ + roleName: roleId, + 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); + }); + }); +} + describe('[Roles]', function() { this.retries(0); @@ -310,4 +330,59 @@ describe('[Roles]', function() { .end(done); }); }); + + describe('POST [/roles.delete]', () => { + let roleWithUser; + let roleWithoutUser; + before(async () => { + roleWithUser = await createRole(`roleWithUser-${ Date.now() }`, 'Users'); + roleWithoutUser = await createRole(`roleWithoutUser-${ Date.now() }`, 'Users'); + + await addUserToRole(roleWithUser._id, login.user); + }); + + it('should delete a role that it is not being used', (done) => { + request.post(api('roles.delete')) + .set(credentials) + .send({ + roleId: roleWithoutUser._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + + it('should NOT delete a role that it is protected', (done) => { + request.post(api('roles.delete')) + .set(credentials) + .send({ + roleId: 'admin', + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.nested.property('error', 'Cannot delete a protected role [error-role-protected]'); + }) + .end(done); + }); + + it('should NOT delete a role that it is being used', (done) => { + request.post(api('roles.delete')) + .set(credentials) + .send({ + roleId: roleWithUser._id, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.nested.property('error', 'Cannot delete role because it\'s in use [error-role-in-use]'); + }) + .end(done); + }); + }); });