Skip to content

implement apply access control to /validate endpoints #2401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ dependencies {
testRuntimeOnly "org.junit.platform:junit-platform-commons:1.8.1"
testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.6.0'
testImplementation group:'org.mockito', name: 'mockito-junit-jupiter', version:'3.6.0'
testImplementation 'org.mockito:mockito-inline:4.11.0'
testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: powermockVersion
testImplementation group: 'org.powermock', name: 'powermock-module-junit4', version: powermockVersion
testImplementation group: 'com.xebialabs.restito', name: 'restito', version: '0.9.3'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Feature: F-1015: Update Case - Start Case Event - Update Code for TTL
And the response has all other details as expected
And the response [contains the TTL.SystemTTL for the case, that has been set to 20 days from today]

@S-1015.1.repeat
@S-1015.1.repeat
Scenario: TTLIncrement is set to "20" for the Case Event and Start Event is invoked on v1_external#/case-details-endpoint/startEventForCaseworkerUsingGET
Given a user with [an active profile in CCD],
And a successful call [to create a case] as in [F-1015_CreateCasePreRequisiteCaseworker_noTTL]
Expand Down Expand Up @@ -461,7 +461,7 @@ Feature: F-1015: Update Case - Start Case Event - Update Code for TTL
And the request [has a TTLIncrement of 20 days configured]
And the request [is configured to trigger an About to Start callback that changes TTL.OverrideTTL]
And it is submitted to call the [Start event creation process to update a case (v1_int caseworker)] operation of [CCD Data Store]

Then a negative response is received
And the response has all other details as expected
And the response [contains the error message indicating unauthorised change to the TTL values]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ Feature: F-1017: Validate Event to Update TTL
# #CCD-3535 & #CCD-3562: Trigger a mid-event callback that makes permitted changes to the TTL values: v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1
#-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

@S-1017.21 #CCD-3535
@S-1017.21 @Ignore #CCD-3535
Scenario: Trigger a mid event callback that changes TTL.Suspended (null -> missing). Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1
Given a user with [an active profile in CCD]
And a user with [a caseworker with an active profile in CCD]
Expand All @@ -274,7 +274,7 @@ Feature: F-1017: Validate Event to Update TTL
And the response [contains the TTL.OverrideTTL from the previouse data]
And the response [does not contain the TTL.Suspended as removed by callback (null -> missing)]

@S-1017.22 #CCD-3535
@S-1017.22 @Ignore #CCD-3535
Scenario: Trigger a mid event callback that changes TTL.Suspended (No -> NO). Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1
Given a user with [an active profile in CCD]
And a user with [a caseworker with an active profile in CCD]
Expand All @@ -297,7 +297,7 @@ Feature: F-1017: Validate Event to Update TTL
And the response [does not contain the TTL.OverrideTTL as removed by callback (null -> missing)]
And the response [contains the adjusted TTL.Suspended from the callback (No -> NO)]

@S-1017.23 #CCD-3535
@S-1017.23 @Ignore #CCD-3535
Scenario: Trigger a mid event callback that changes TTL.Suspended (Yes -> YES). Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1
Given a user with [an active profile in CCD]
And a user with [a caseworker with an active profile in CCD]
Expand All @@ -321,7 +321,7 @@ Feature: F-1017: Validate Event to Update TTL
And the response [contains the adjusted TTL.Suspended from the callback (Yes -> YES)]


@S-1017.25 #CCD-3562
@S-1017.25 @Ignore #CCD-3562
Scenario: Trigger a mid event callback that has TTL missing. Mid Event is invoked on v1_external#/citizen/case-details-endpoint/validateCaseDetailsUsingPOST_1
Given a user with [an active profile in CCD]
And a successful call [to create a case] as in [CreateCase_TTLCaseType_PreRequisiteCitizen]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,6 @@
"expectedResponse": {
"body": {
"data": {
"TTL": {
"SystemTTL": "${[scenarioContext][customValues][dateTwentyDaysFromToday]}",
"OverrideTTL": "${[scenarioContext][customValues][dateGreaterThanTTLGuardDate]}"
},
"TextField" : "[[ANYTHING_PRESENT]]"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@
"expectedResponse": {
"body": {
"data": {
"TTL": {
"SystemTTL": "${[scenarioContext][customValues][dateTwentyDaysFromToday]}",
"Suspended": "NO"
},
"TextField" : "[[ANYTHING_PRESENT]]"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@
"expectedResponse": {
"body": {
"data": {
"TTL": {
"SystemTTL": "${[scenarioContext][customValues][dateTwentyDaysFromToday]}",
"Suspended": "YES"
},
"TextField" : "[[ANYTHING_PRESENT]]"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,6 @@
"expectedResponse": {
"body": {
"data": {
"TTL" : {
"SystemTTL" : "${[scenarioContext][customValues][dateTwentyDaysFromToday]}",
"OverrideTTL" : null,
"Suspended" : "No"
},
"TextField" : "[[ANYTHING_PRESENT]]"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
import uk.gov.hmcts.ccd.domain.service.caselinking.CaseLinkService;
import uk.gov.hmcts.ccd.domain.service.common.CaseDataService;
import uk.gov.hmcts.ccd.domain.service.common.CasePostStateService;
import uk.gov.hmcts.ccd.domain.service.common.CaseTypeService;
import uk.gov.hmcts.ccd.domain.service.common.EventTriggerService;
import uk.gov.hmcts.ccd.domain.service.createcase.DefaultCreateCaseOperation;
import uk.gov.hmcts.ccd.domain.service.createcase.SubmitCaseTransaction;
import uk.gov.hmcts.ccd.domain.service.processor.GlobalSearchProcessorService;
import uk.gov.hmcts.ccd.domain.service.stdapi.CallbackInvoker;
import uk.gov.hmcts.ccd.domain.service.supplementarydata.SupplementaryDataUpdateOperation;
import uk.gov.hmcts.ccd.domain.service.validate.CaseDataIssueLogger;
import uk.gov.hmcts.ccd.domain.service.validate.DefaultValidateCaseFieldsOperation;
import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation;
import uk.gov.hmcts.ccd.domain.types.sanitiser.CaseSanitiser;

Expand All @@ -47,8 +47,9 @@ public ContractTestCreateCaseOperation(@Qualifier(DefaultUserRepository.QUALIFIE
EventTokenService eventTokenService,
CaseDataService caseDataService,
SubmitCaseTransaction submitCaseTransaction,
CaseSanitiser caseSanitiser, CaseTypeService caseTypeService,
CaseSanitiser caseSanitiser,
CallbackInvoker callbackInvoker,
@Qualifier(DefaultValidateCaseFieldsOperation.QUALIFIER)
ValidateCaseFieldsOperation validateCaseFieldsOperation,
CasePostStateService casePostStateService,
@Qualifier(DefaultDraftGateway.QUALIFIER) DraftGateway draftGateway,
Expand All @@ -61,7 +62,7 @@ public ContractTestCreateCaseOperation(@Qualifier(DefaultUserRepository.QUALIFIE
CaseLinkService caseLinkService,
TimeToLiveService timeToLiveService) {
super(userRepository, caseDefinitionRepository, eventTriggerService, eventTokenService, caseDataService,
submitCaseTransaction, caseSanitiser, caseTypeService, callbackInvoker, validateCaseFieldsOperation,
submitCaseTransaction, caseSanitiser, callbackInvoker, validateCaseFieldsOperation,
casePostStateService, draftGateway, caseDataIssueLogger, globalSearchProcessorService,
supplementaryDataUpdateOperation, supplementaryDataValidator, caseLinkService, timeToLiveService);
this.contractTestSecurityUtils = contractTestSecurityUtils;
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/uk/gov/hmcts/ccd/config/JacksonUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

public final class JacksonUtils {

public static final String DATA = "data";

private JacksonUtils() {
}

Expand Down Expand Up @@ -198,4 +200,10 @@ private static String getValue(@NonNull JsonNode jsonNode) {
}
return returnValue;
}

public static Map<String, JsonNode> convertValueInDataField(Object from) {
Map<String, JsonNode> dataNode = new HashMap<>();
dataNode.put(DATA, MAPPER.valueToTree(convertValue(from)));
return dataNode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@
import uk.gov.hmcts.ccd.domain.service.caselinking.CaseLinkService;
import uk.gov.hmcts.ccd.domain.service.common.CaseDataService;
import uk.gov.hmcts.ccd.domain.service.common.CasePostStateService;
import uk.gov.hmcts.ccd.domain.service.common.CaseTypeService;
import uk.gov.hmcts.ccd.domain.service.common.EventTriggerService;
import uk.gov.hmcts.ccd.domain.service.processor.GlobalSearchProcessorService;
import uk.gov.hmcts.ccd.domain.service.stdapi.CallbackInvoker;
import uk.gov.hmcts.ccd.domain.service.supplementarydata.SupplementaryDataUpdateOperation;
import uk.gov.hmcts.ccd.domain.service.validate.CaseDataIssueLogger;
import uk.gov.hmcts.ccd.domain.service.validate.DefaultValidateCaseFieldsOperation;
import uk.gov.hmcts.ccd.domain.service.validate.OperationContext;
import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation;
import uk.gov.hmcts.ccd.domain.types.sanitiser.CaseSanitiser;
import uk.gov.hmcts.ccd.endpoint.exceptions.CallbackException;
Expand All @@ -58,7 +59,6 @@ public class DefaultCreateCaseOperation implements CreateCaseOperation {
private final CaseDataService caseDataService;
private final SubmitCaseTransaction submitCaseTransaction;
private final CaseSanitiser caseSanitiser;
private final CaseTypeService caseTypeService;
private final CallbackInvoker callbackInvoker;
private final ValidateCaseFieldsOperation validateCaseFieldsOperation;
private final DraftGateway draftGateway;
Expand All @@ -79,8 +79,8 @@ public DefaultCreateCaseOperation(@Qualifier(CachedUserRepository.QUALIFIER) fin
final CaseDataService caseDataService,
final SubmitCaseTransaction submitCaseTransaction,
final CaseSanitiser caseSanitiser,
final CaseTypeService caseTypeService,
final CallbackInvoker callbackInvoker,
@Qualifier(DefaultValidateCaseFieldsOperation.QUALIFIER)
final ValidateCaseFieldsOperation validateCaseFieldsOperation,
final CasePostStateService casePostStateService,
@Qualifier(CachedDraftGateway.QUALIFIER) final DraftGateway draftGateway,
Expand All @@ -97,7 +97,6 @@ public DefaultCreateCaseOperation(@Qualifier(CachedUserRepository.QUALIFIER) fin
this.eventTokenService = eventTokenService;
this.submitCaseTransaction = submitCaseTransaction;
this.caseSanitiser = caseSanitiser;
this.caseTypeService = caseTypeService;
this.caseDataService = caseDataService;
this.callbackInvoker = callbackInvoker;
this.validateCaseFieldsOperation = validateCaseFieldsOperation;
Expand Down Expand Up @@ -144,7 +143,7 @@ public CaseDetails createCaseDetails(final String caseTypeId,
caseTypeDefinition.getJurisdictionDefinition(),
caseTypeDefinition);

validateCaseFieldsOperation.validateCaseDetails(caseTypeId, caseDataContent);
validateCaseFieldsOperation.validateCaseDetails(new OperationContext(caseTypeId, caseDataContent));

final CaseDetails newCaseDetails = new CaseDetails();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import uk.gov.hmcts.ccd.domain.service.stdapi.AboutToSubmitCallbackResponse;
import uk.gov.hmcts.ccd.domain.service.stdapi.CallbackInvoker;
import uk.gov.hmcts.ccd.domain.service.validate.CaseDataIssueLogger;
import uk.gov.hmcts.ccd.domain.service.validate.DefaultValidateCaseFieldsOperation;
import uk.gov.hmcts.ccd.domain.service.validate.ValidateCaseFieldsOperation;
import uk.gov.hmcts.ccd.domain.types.sanitiser.CaseSanitiser;
import uk.gov.hmcts.ccd.endpoint.exceptions.BadRequestException;
Expand Down Expand Up @@ -116,6 +117,7 @@ public CreateCaseEventService(@Qualifier(CachedUserRepository.QUALIFIER) final U
final CallbackInvoker callbackInvoker,
final UIDService uidService,
final SecurityClassificationServiceImpl securityClassificationService,
@Qualifier(DefaultValidateCaseFieldsOperation.QUALIFIER)
final ValidateCaseFieldsOperation validateCaseFieldsOperation,
final UserAuthorisation userAuthorisation,
final FieldProcessorService fieldProcessorService,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package uk.gov.hmcts.ccd.domain.service.createevent;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -52,9 +51,7 @@ public MidEventCallback(CallbackInvoker callbackInvoker,
}

@Transactional
public JsonNode invoke(String caseTypeId,
CaseDataContent content,
String pageId) {
public Map<String, JsonNode> invoke(String caseTypeId, CaseDataContent content, String pageId) {
if (!isBlank(pageId)) {
Event event = content.getEvent();
final CaseTypeDefinition caseTypeDefinition = getCaseType(caseTypeId);
Expand Down Expand Up @@ -91,11 +88,10 @@ public JsonNode invoke(String caseTypeId,
caseDetailsBefore,
currentOrNewCaseDetails,
content.getIgnoreWarning());

return dataJsonNode(caseDetailsFromMidEventCallback.getData());
return caseDetailsFromMidEventCallback.getData();
}
}
return dataJsonNode(content.getData());
return content.getData();
}

private void removeNextPageFieldData(CaseDetails currentCaseDetails, Integer order,
Expand All @@ -112,13 +108,6 @@ private void removeNextPageFieldData(CaseDetails currentCaseDetails, Integer ord
}
}

private JsonNode dataJsonNode(Map<String, JsonNode> data) {
ObjectMapper mapper = new ObjectMapper();
ObjectNode objectNode = mapper.createObjectNode();
objectNode.set("data", mapper.valueToTree(data));
return objectNode;
}

private CaseTypeDefinition getCaseType(String caseTypeId) {
final CaseTypeDefinition caseTypeDefinition = caseDefinitionRepository.getCaseType(caseTypeId);
if (caseTypeDefinition == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package uk.gov.hmcts.ccd.domain.service.validate;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import uk.gov.hmcts.ccd.config.JacksonUtils;
import uk.gov.hmcts.ccd.data.definition.CachedCaseDefinitionRepository;
import uk.gov.hmcts.ccd.data.definition.CaseDefinitionRepository;
import uk.gov.hmcts.ccd.domain.model.casedataaccesscontrol.AccessProfile;
import uk.gov.hmcts.ccd.domain.model.definition.CaseTypeDefinition;
import uk.gov.hmcts.ccd.domain.model.std.CaseDataContent;
import uk.gov.hmcts.ccd.domain.service.common.AccessControlService;
import uk.gov.hmcts.ccd.domain.service.common.CaseAccessService;
import uk.gov.hmcts.ccd.endpoint.exceptions.ValidationException;

import java.util.Map;
import java.util.Set;

import static com.google.common.collect.Maps.newHashMap;
import static uk.gov.hmcts.ccd.domain.service.common.AccessControlService.CAN_READ;

@Service
@Slf4j
@Qualifier(AuthorisedValidateCaseFieldsOperation.QUALIFIER)
public class AuthorisedValidateCaseFieldsOperation implements ValidateCaseFieldsOperation {
public static final String QUALIFIER = "authorised";

private final AccessControlService accessControlService;
private final CaseDefinitionRepository caseDefinitionRepository;
private final CaseAccessService caseAccessService;
private final ValidateCaseFieldsOperation validateCaseFieldsOperation;

public AuthorisedValidateCaseFieldsOperation(AccessControlService accessControlService,
@Qualifier(CachedCaseDefinitionRepository.QUALIFIER)
CaseDefinitionRepository caseDefinitionRepository,
CaseAccessService caseAccessService,
@Qualifier(ClassifiedValidateCaseFieldsOperation.QUALIFIER)
ValidateCaseFieldsOperation validateCaseFieldsOperation) {
this.accessControlService = accessControlService;
this.caseDefinitionRepository = caseDefinitionRepository;
this.caseAccessService = caseAccessService;
this.validateCaseFieldsOperation = validateCaseFieldsOperation;
}

@Override
public Map<String, JsonNode> validateCaseDetails(OperationContext operationContext) {
validateCaseFieldsOperation.validateCaseDetails(operationContext);

CaseDataContent content = operationContext.content();
String caseTypeId = operationContext.caseTypeId();

String caseReference = content.getCaseReference();
Set<AccessProfile> accessProfiles = StringUtils.isNotEmpty(caseReference)
? caseAccessService.getAccessProfilesByCaseReference(caseReference) :
caseAccessService.getCaseCreationRoles(caseTypeId);

verifyReadAccess(caseTypeId, content, accessProfiles);

return content.getData();
}

@Override
public void validateData(Map<String, JsonNode> data, CaseTypeDefinition caseTypeDefinition,
CaseDataContent content) {
validateCaseFieldsOperation.validateData(data, caseTypeDefinition, content);
}

private void verifyReadAccess(final String caseTypeId, CaseDataContent content, Set<AccessProfile> accessProfiles) {
final CaseTypeDefinition caseTypeDefinition = getCaseDefinitionType(caseTypeId);

if (content.getData() == null) {
content.setData(newHashMap());
return;
}

if (!accessControlService.canAccessCaseTypeWithCriteria(
caseTypeDefinition,
accessProfiles,
CAN_READ)) {
content.setData(newHashMap());
return;
}

content.setData(JacksonUtils.convertValueInDataField(
accessControlService.filterCaseFieldsByAccess(
JacksonUtils.convertValueJsonNode(content.getData()),
caseTypeDefinition.getCaseFieldDefinitions(),
accessProfiles,
CAN_READ,
false)));
}

private CaseTypeDefinition getCaseDefinitionType(String caseTypeId) {
final CaseTypeDefinition caseTypeDefinition = caseDefinitionRepository.getCaseType(caseTypeId);
if (caseTypeDefinition == null) {
throw new ValidationException("Cannot find case type definition for " + caseTypeId);
}
return caseTypeDefinition;
}
}
Loading