Skip to content

Commit

Permalink
Add a 'permission' query parameter (filter) to the Business Unit refe…
Browse files Browse the repository at this point in the history
…rence data endpoint (#382)
  • Loading branch information
RustyHMCTS committed Jun 10, 2024
1 parent f1086af commit 79533c3
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package uk.gov.hmcts.opal.controllers;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
Expand All @@ -9,13 +10,16 @@
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import uk.gov.hmcts.opal.authorisation.model.UserState;
import uk.gov.hmcts.opal.dto.search.BusinessUnitSearchDto;
import uk.gov.hmcts.opal.entity.BusinessUnitEntity;
import uk.gov.hmcts.opal.entity.projection.BusinessUnitReferenceData;
import uk.gov.hmcts.opal.service.opal.BusinessUnitService;
import uk.gov.hmcts.opal.service.opal.UserStateService;

import static java.util.Collections.singletonList;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
Expand All @@ -35,6 +39,9 @@ class BusinessUnitControllerIntegrationTest {
@Qualifier("businessUnitService")
BusinessUnitService businessUnitService;

@MockBean
UserStateService userStateService;

@Test
void testGetBusinessUnitById() throws Exception {
BusinessUnitEntity businessUnitEntity = createBusinessUnitEntity();
Expand Down Expand Up @@ -92,7 +99,7 @@ void testPostBusinessUnitsSearch_WhenBusinessUnitDoesNotExist() throws Exception
}

@Test
void testGetCourtRefData() throws Exception {
void testGetBusinessUnitRefData() throws Exception {
BusinessUnitReferenceData refData = createBusinessUnitRefData();

when(businessUnitService.getReferenceData(any())).thenReturn(singletonList(refData));
Expand All @@ -110,6 +117,43 @@ void testGetCourtRefData() throws Exception {
.andExpect(jsonPath("$.refData[0].opalDomain").value("Fines"));
}

@Test
void testGetBusinessUnitRefData_Permission_success() throws Exception {
BusinessUnitReferenceData refData = createBusinessUnitRefData();
UserState userState = Mockito.mock(UserState.class);

when(businessUnitService.getReferenceData(any())).thenReturn(singletonList(refData));
when(userStateService.getUserStateUsingAuthToken(anyString())).thenReturn(userState);
when(userState.allRolesWithPermission(any())).thenReturn(new TestUserRoles(true));

mockMvc.perform(get("/api/business-unit/ref-data/?permission=MANUAL_ACCOUNT_CREATION")
.header("authorization", "Bearer some_value"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.count").value(1))
.andExpect(jsonPath("$.refData[0].businessUnitId").value(1))
.andExpect(jsonPath("$.refData[0].businessUnitName").value("Business Unit 001"))
.andExpect(jsonPath("$.refData[0].businessUnitCode").value("AAAA"))
.andExpect(jsonPath("$.refData[0].businessUnitType").value("LARGE UNIT"))
.andExpect(jsonPath("$.refData[0].accountNumberPrefix").value("XX"))
.andExpect(jsonPath("$.refData[0].opalDomain").value("Fines"));
}

@Test
void testGetBusinessUnitRefData_Permission_empty() throws Exception {
BusinessUnitReferenceData refData = createBusinessUnitRefData();
UserState userState = Mockito.mock(UserState.class);

when(businessUnitService.getReferenceData(any())).thenReturn(singletonList(refData));
when(userStateService.getUserStateUsingAuthToken(anyString())).thenReturn(userState);
when(userState.allRolesWithPermission(any())).thenReturn(new TestUserRoles(false));

mockMvc.perform(get("/api/business-unit/ref-data/?permission=MANUAL_ACCOUNT_CREATION")
.header("authorization", "Bearer some_value"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.count").value(0));
}

private BusinessUnitEntity createBusinessUnitEntity() {
return BusinessUnitEntity.builder()
Expand Down Expand Up @@ -157,4 +201,17 @@ public String getOpalDomain() {
}
};
}

private class TestUserRoles implements UserState.UserRoles {
private final boolean contains;

public TestUserRoles(boolean contains) {
this.contains = contains;
}

@Override
public boolean containsBusinessUnit(Short businessUnitId) {
return contains;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

public enum Permissions {
ACCOUNT_ENQUIRY(54, "Account Enquiry"),
ACCOUNT_ENQUIRY_NOTES(41, "Account Enquiry - Account Notes");
ACCOUNT_ENQUIRY_NOTES(41, "Account Enquiry - Account Notes"),
MANUAL_ACCOUNT_CREATION(35, "Manual Account Creation");

public final long id;

Expand Down
34 changes: 34 additions & 0 deletions src/main/java/uk/gov/hmcts/opal/authorisation/model/UserState.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Builder
@EqualsAndHashCode
Expand All @@ -37,6 +38,11 @@ public boolean noRoleHasPermission(Permissions permission) {
return !anyRoleHasPermission(permission);
}

public UserRoles allRolesWithPermission(Permissions permission) {
return new UserRolesImpl(
roles.stream().filter(r -> r.hasPermission(permission)).collect(Collectors.toSet()));
}

public boolean hasRoleWithPermission(short roleBusinessUnitId, Permissions permission) {
return roles.stream()
.filter(r -> r.matchesBusinessUnitId(roleBusinessUnitId))
Expand All @@ -51,6 +57,24 @@ public Optional<Role> getRoleForBusinessUnit(Short businessUnitId) {
.findFirst();
}

public static interface UserRoles {
boolean containsBusinessUnit(Short businessUnitId);
}

public static class UserRolesImpl implements UserRoles {
private final Set<Role> roles;
private final Set<Short> businessUnits;

public UserRolesImpl(Set<Role> roles) {
this.roles = roles;
businessUnits = roles.stream().map(r -> r.getBusinessUnitId()).collect(Collectors.toSet());
}

public boolean containsBusinessUnit(Short businessUnitId) {
return businessUnits.contains(businessUnitId);
}
}

public static class DeveloperUserState extends UserState {
private static final Optional<Role> DEV_ROLE = Optional.of(new DeveloperRole());

Expand All @@ -67,5 +91,15 @@ public boolean anyRoleHasPermission(Permissions permission) {
public Optional<Role> getRoleForBusinessUnit(Short businessUnitId) {
return DEV_ROLE;
}

@Override
public UserRoles allRolesWithPermission(Permissions permission) {
return new UserRoles() {
@Override
public boolean containsBusinessUnit(Short businessUnitId) {
return true;
}
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import uk.gov.hmcts.opal.authorisation.model.Permissions;
import uk.gov.hmcts.opal.dto.reference.BusinessUnitReferenceDataResults;
import uk.gov.hmcts.opal.dto.search.BusinessUnitSearchDto;
import uk.gov.hmcts.opal.entity.BusinessUnitEntity;
import uk.gov.hmcts.opal.entity.projection.BusinessUnitReferenceData;
import uk.gov.hmcts.opal.service.BusinessUnitServiceInterface;
import uk.gov.hmcts.opal.service.opal.BusinessUnitService;
import uk.gov.hmcts.opal.service.opal.UserStateService;

import java.util.List;
import java.util.Optional;

import static uk.gov.hmcts.opal.util.HttpUtil.buildResponse;
import static uk.gov.hmcts.opal.util.PermissionUtil.filterBusinessUnitsByPermission;


@RestController
Expand All @@ -35,11 +40,14 @@ public class BusinessUnitController {

private final BusinessUnitService opalBusinessUnitService;

private final UserStateService userStateService;

public BusinessUnitController(
@Qualifier("businessUnitService") BusinessUnitServiceInterface businessUnitService,
BusinessUnitService opalBusinessUnitService) {
BusinessUnitService opalBusinessUnitService, UserStateService userStateService) {
this.businessUnitService = businessUnitService;
this.opalBusinessUnitService = opalBusinessUnitService;
this.userStateService = userStateService;
}

@GetMapping(value = "/{businessUnitId}")
Expand Down Expand Up @@ -67,10 +75,13 @@ public ResponseEntity<List<BusinessUnitEntity>> postBusinessUnitsSearch(
@GetMapping(value = {"/ref-data", "/ref-data/", "/ref-data/{filter}"})
@Operation(summary = "Returns Business Units as reference data with an optional filter applied")
public ResponseEntity<BusinessUnitReferenceDataResults> getBusinessUnitRefData(
@PathVariable Optional<String> filter) {
log.info(":GET:getBusinessUnitRefData: query: \n{}", filter);
@PathVariable Optional<String> filter, @RequestParam Optional<Permissions> permission,
@RequestHeader(value = "Authorization", required = false) String authHeaderValue) {

log.info(":GET:getBusinessUnitRefData: permission: {}, query: \n{}", permission, filter);

List<BusinessUnitReferenceData> refData = opalBusinessUnitService.getReferenceData(filter);
List<BusinessUnitReferenceData> refData = filterBusinessUnitsByPermission(userStateService,
opalBusinessUnitService.getReferenceData(filter), permission, authHeaderValue);

log.info(":GET:getBusinessUnitRefData: business unit reference data count: {}", refData.size());
return ResponseEntity.ok(BusinessUnitReferenceDataResults.builder().refData(refData).build());
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/uk/gov/hmcts/opal/entity/BusinessUnitRef.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package uk.gov.hmcts.opal.entity;

public interface BusinessUnitRef {
Short getBusinessUnitId();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package uk.gov.hmcts.opal.entity.projection;

public interface BusinessUnitReferenceData {
import uk.gov.hmcts.opal.entity.BusinessUnitRef;

public interface BusinessUnitReferenceData extends BusinessUnitRef {

Short getBusinessUnitId();

Expand Down
22 changes: 21 additions & 1 deletion src/main/java/uk/gov/hmcts/opal/util/PermissionUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
import uk.gov.hmcts.opal.authorisation.model.Permissions;
import uk.gov.hmcts.opal.authorisation.model.Role;
import uk.gov.hmcts.opal.authorisation.model.UserState;
import uk.gov.hmcts.opal.entity.BusinessUnitRef;
import uk.gov.hmcts.opal.service.opal.UserStateService;

public class PermissionUtil {
import java.util.List;
import java.util.Optional;

public class PermissionUtil {

public static Role getRequiredRole(UserState userState, Short businessUnitId) {
return userState.getRoleForBusinessUnit(businessUnitId).orElseThrow(() -> new
Expand All @@ -27,4 +31,20 @@ public static boolean checkAnyRoleHasPermission(UserState userState, Permissions
return true;
}

public static <B extends BusinessUnitRef> List<B> filterBusinessUnitsByPermission(
UserStateService userStateService, List<B> refData,
Optional<Permissions> optPermission, String authHeaderValue) {

return optPermission.map(
permission -> {
UserState.UserRoles userRoles = userStateService
.getUserStateUsingAuthToken(authHeaderValue)
.allRolesWithPermission(permission);
return refData
.stream()
.filter(bu -> userRoles
.containsBusinessUnit(bu.getBusinessUnitId()))
.toList();
}).orElse(refData);
}
}
Loading

0 comments on commit 79533c3

Please sign in to comment.