Skip to content

Commit fbe0b18

Browse files
committed
Added a permission for editing member roles, disallow removal of edit permissions from admin, and fixed issues with user edit/delete.
1 parent 51f334a commit fbe0b18

File tree

15 files changed

+116
-29
lines changed

15 files changed

+116
-29
lines changed

server/src/main/java/com/objectcomputing/checkins/services/memberprofile/MemberProfileServicesImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public boolean deleteProfile(@NotNull UUID id) {
160160
} else if (!teamMemberServices.findByFields(null, id, null).isEmpty()) {
161161
throw new BadArgException(String.format("User %s cannot be deleted since TeamMember record(s) exist", MemberProfileUtils.getFullName(memberProfile)));
162162
} else if (!userRoles.isEmpty()) {
163-
throw new BadArgException(String.format("User %s cannot be deleted since user has PDL role", MemberProfileUtils.getFullName(memberProfile)));
163+
throw new BadArgException(String.format("User %s cannot be deleted since user has one or more roles", MemberProfileUtils.getFullName(memberProfile)));
164164
}
165165

166166
// Update PDL ID for all associated members before termination

server/src/main/java/com/objectcomputing/checkins/services/permissions/Permission.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public enum Permission {
1919
CAN_IMPERSONATE_MEMBERS("Impersonate organization members", "Security"),
2020
CAN_VIEW_ROLE_PERMISSIONS("View role permissions", "Security"),
2121
CAN_ASSIGN_ROLE_PERMISSIONS("Assign role permissions", "Security"),
22+
CAN_EDIT_MEMBER_ROLES("Edit member roles", "Security"),
2223
CAN_VIEW_PERMISSIONS("View all permissions", "Security"),
2324
CAN_VIEW_SKILLS_REPORT("View skills report", "Reporting"),
2425
CAN_VIEW_RETENTION_REPORT("View retention report", "Reporting"),

server/src/main/java/com/objectcomputing/checkins/services/role/member_roles/MemberRoleController.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.objectcomputing.checkins.services.role.member_roles;
22

3+
import com.objectcomputing.checkins.services.permissions.Permission;
4+
import com.objectcomputing.checkins.services.permissions.RequiredPermission;
5+
36
import io.micronaut.http.HttpResponse;
47
import io.micronaut.http.annotation.*;
58
import io.micronaut.scheduling.TaskExecutors;
@@ -29,13 +32,15 @@ HttpResponse<List<MemberRole>> getAllAssignedMemberRoles() {
2932
}
3033

3134
@Delete("/{roleId}/{memberId}")
35+
@RequiredPermission(Permission.CAN_EDIT_MEMBER_ROLES)
3236
HttpResponse<?> deleteMemberRole(@NotNull UUID roleId, @NotNull UUID memberId){
3337
memberRoleServices.delete(new MemberRoleId(memberId, roleId));
3438
return HttpResponse.ok();
3539
}
3640

3741
@Post
3842
@CacheInvalidate(cacheNames = {"role-permission-cache"})
43+
@RequiredPermission(Permission.CAN_EDIT_MEMBER_ROLES)
3944
HttpResponse<MemberRole> saveMemberRole(@NotNull @Body MemberRoleId id){
4045
MemberRole memberRole = memberRoleServices.saveByIds(id.getMemberId(), id.getRoleId());
4146
return HttpResponse.ok(memberRole);

server/src/main/java/com/objectcomputing/checkins/services/role/role_permissions/RolePermissionServicesImpl.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.objectcomputing.checkins.services.role.role_permissions;
22

3+
import com.objectcomputing.checkins.exceptions.BadArgException;
34
import com.objectcomputing.checkins.services.permissions.Permission;
45
import com.objectcomputing.checkins.services.permissions.PermissionDTO;
56
import com.objectcomputing.checkins.services.permissions.PermissionServices;
67
import com.objectcomputing.checkins.services.role.Role;
8+
import com.objectcomputing.checkins.services.role.RoleType;
79
import com.objectcomputing.checkins.services.role.RoleServices;
810
import io.micronaut.cache.annotation.CacheConfig;
911
import io.micronaut.cache.annotation.CacheInvalidate;
@@ -70,6 +72,13 @@ public RolePermission save(UUID roleId, Permission permissionId) {
7072
@CacheInvalidate(all = true)
7173
@Override
7274
public void delete(UUID roleId, Permission permissionId) {
75+
// Disallow removal of Assign Role Permissions from ADMIN
76+
if (permissionId == Permission.CAN_ASSIGN_ROLE_PERMISSIONS) {
77+
Role role = roleServices.read(roleId);
78+
if (role.getRole().equals(RoleType.Constants.ADMIN_ROLE)) {
79+
throw new BadArgException("Cannot remove Assign Role Permissions from ADMIN");
80+
}
81+
}
7382
rolePermissionRepository.deleteByIds(roleId.toString(), permissionId);
7483
}
7584

server/src/main/resources/db/dev/R__Load_testing_data.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,11 @@ VALUES
633633
('fa2319a4-8db6-4b83-bba7-70a3e4d7670f', '2024-05-21', '1b4f99da-ef70-4a76-9b37-8bb783b749ad', PGP_SYM_ENCRYPT('internal #9','${aeskey}'), PGP_SYM_ENCRYPT('extenal #9','${aeskey}'), 4, 5);
634634

635635
-- Admin Permissions
636+
insert into role_permissions
637+
(roleid, permission)
638+
values
639+
('e8a4fff8-e984-4e59-be84-a713c9fa8d23', 'CAN_EDIT_MEMBER_ROLES');
640+
636641
insert into role_permissions
637642
(roleid, permission)
638643
values

server/src/test/java/com/objectcomputing/checkins/services/fixture/PermissionFixture.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public interface PermissionFixture extends RolePermissionFixture {
4747

4848
// Add ADMIN Permissions here
4949
List<Permission> adminPermissions = List.of(
50+
Permission.CAN_EDIT_MEMBER_ROLES,
5051
Permission.CAN_VIEW_FEEDBACK_REQUEST,
5152
Permission.CAN_CREATE_FEEDBACK_REQUEST,
5253
Permission.CAN_DELETE_FEEDBACK_REQUEST,

server/src/test/java/com/objectcomputing/checkins/services/member_role/MemberRoleControllerTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.micronaut.http.HttpStatus;
1515
import io.micronaut.http.client.HttpClient;
1616
import io.micronaut.http.client.annotation.Client;
17+
import io.micronaut.http.client.exceptions.HttpClientResponseException;
1718
import jakarta.inject.Inject;
1819
import org.junit.jupiter.api.Test;
1920

@@ -69,6 +70,18 @@ void testDeleteMemberRole() {
6970
assertEquals(HttpStatus.OK, response.getStatus());
7071
}
7172

73+
@Test
74+
void testDeleteMemberRoleNoPermission() {
75+
MemberProfile member = createADefaultMemberProfile();
76+
77+
Role memberRole = createAndAssignRole(RoleType.MEMBER, member);
78+
79+
final HttpRequest<Object> request = HttpRequest.DELETE(String.format("/%s/%s", memberRole.getId(), member.getId()))
80+
.basicAuth(member.getWorkEmail(), RoleType.Constants.MEMBER_ROLE);
81+
HttpClientResponseException e = assertThrows(HttpClientResponseException.class, () -> client.toBlocking().exchange(request, MemberRole.class));
82+
assertEquals(HttpStatus.FORBIDDEN, e.getStatus());
83+
}
84+
7285
@Test
7386
void testSaveMemberRole() {
7487
MemberProfile member = createADefaultMemberProfile();
@@ -89,4 +102,17 @@ void testSaveMemberRole() {
89102
assertEquals(adminRole.getId(), response.getBody().get().getMemberRoleId().getRoleId());
90103
}
91104

105+
@Test
106+
void testSaveMemberRoleNoPermission() {
107+
MemberProfile member = createADefaultMemberProfile();
108+
Role memberRole = createAndAssignRole(RoleType.MEMBER, member);
109+
110+
MemberRoleId memberRoleId = new MemberRoleId(member.getId(), memberRole.getId());
111+
112+
final HttpRequest<?> request = HttpRequest.POST("", memberRoleId)
113+
.basicAuth(member.getWorkEmail(), RoleType.Constants.MEMBER_ROLE);
114+
115+
HttpClientResponseException e = assertThrows(HttpClientResponseException.class, () -> client.toBlocking().exchange(request, MemberRole.class));
116+
assertEquals(HttpStatus.FORBIDDEN, e.getStatus());
117+
}
92118
}

server/src/test/java/com/objectcomputing/checkins/services/role/role_permissions/RolePermissionsControllerTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,19 @@ void testRemovePermissionFromRole() {
112112
assertEquals(HttpStatus.OK, response.getStatus());
113113
assertEquals(0, getRolePermissionRepository().findByIds(memberRole.getId().toString(), birthdayPermission).size());
114114
}
115+
116+
@Test
117+
void testRemoveAssignRolePermissionFromAdminRole() {
118+
MemberProfile sender = createADefaultMemberProfile();
119+
assignAdminRole(sender);
120+
121+
Role adminRole = getRoleRepository().findByRole(RoleType.ADMIN.name()).orElseThrow();
122+
Permission permission = Permission.CAN_ASSIGN_ROLE_PERMISSIONS;
123+
124+
final HttpRequest<?> request = HttpRequest.DELETE("/", new RolePermissionDTO(adminRole.getId(), permission))
125+
.basicAuth(sender.getWorkEmail(), RoleType.Constants.ADMIN_ROLE);
126+
HttpClientResponseException e = assertThrows(HttpClientResponseException.class,
127+
() -> client.toBlocking().exchange(request, Map.class));
128+
assertEquals(HttpStatus.BAD_REQUEST, e.getStatus());
129+
}
115130
}

web-ui/src/components/admin/permissions/Permissions.jsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@ import { useMediaQuery } from '@mui/material';
22
import React from 'react';
33
import DesktopTable from './DesktopTable';
44
import MobileTable from './MobileTable';
5+
import { AppContext } from '../../../context/AppContext';
6+
import {
7+
selectHasPermissionAssignmentPermission,
8+
noPermission,
9+
} from '../../../context/selectors';
510

611
export default function Permissions() {
12+
const { state } = useContext(AppContext);
713
const showDesktop = useMediaQuery('(min-width:650px)', { noSsr: true });
814

9-
return <div>{showDesktop ? <DesktopTable /> : <MobileTable />}</div>;
15+
return selectHasPermissionAssignmentPermission(state) ?
16+
(<div>{showDesktop ? <DesktopTable /> : <MobileTable />}</div>) :
17+
(<h3>{noPermission}</h3>);
1018
}

web-ui/src/components/admin/roles/Roles.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
updateRole
1414
} from '../../../api/roles';
1515
import {
16-
selectHasRoleAssignmentPermission,
16+
selectCanEditMemberRolesPermission,
1717
noPermission,
1818
} from '../../../context/selectors';
1919
import RoleUserCards from './RoleUserCards';
@@ -215,7 +215,7 @@ const Roles = () => {
215215
setEditedRole({ ...editedRole, description: event?.target?.value });
216216
};
217217

218-
return selectHasRoleAssignmentPermission(state) ? (
218+
return selectCanEditMemberRolesPermission(state) ? (
219219
<div className="roles-content">
220220
<div className="roles">
221221
<div className="roles-top">

0 commit comments

Comments
 (0)