Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,48 @@ void setupNonCaseworkerUser() {
.header(SERVICE_AUTH_HEADER, testUtil.getS2sAuth());
}

@Test
void shouldReturnNotFoundWhenDifferentUserAttemptsToGetAnotherUsersTask() throws IOException {
BundleDTO bundle = testUtil.getTestBundle();
DocumentTaskDTO documentTask = new DocumentTaskDTO();
documentTask.setBundle(bundle);

Response createTaskResponse = testUtil.authRequest()
.body(convertObjectToJsonBytes(documentTask))
.post(DOCUMENT_TASKS_ENDPOINT);

assertEquals(201, createTaskResponse.getStatusCode(),
"Primary user should be able to create a task.");
String taskId = createTaskResponse.getBody().jsonPath().getString("id");

Response getTaskResponse = nonCaseworkerRequest
.get(DOCUMENT_TASKS_ENDPOINT + "/" + taskId);

assertEquals(404, getTaskResponse.getStatusCode(),
"A different authenticated user must not be able to retrieve another user's task (IDOR).");
}

@Test
void shouldReturnTaskWhenOwnerRequestsTheirOwnTask() throws IOException {
BundleDTO bundle = testUtil.getTestBundle();
DocumentTaskDTO documentTask = new DocumentTaskDTO();
documentTask.setBundle(bundle);

Response createTaskResponse = testUtil.authRequest()
.body(convertObjectToJsonBytes(documentTask))
.post(DOCUMENT_TASKS_ENDPOINT);

assertEquals(201, createTaskResponse.getStatusCode(),
"Primary user should be able to create a task.");
String taskId = createTaskResponse.getBody().jsonPath().getString("id");

Response getTaskResponse = testUtil.authRequest()
.get(DOCUMENT_TASKS_ENDPOINT + "/" + taskId);

assertEquals(200, getTaskResponse.getStatusCode(),
"The task owner must be able to retrieve their own task.");
}

@Test
void shouldFailTaskWhenUserHasNoCaseworkerRole() throws IOException, InterruptedException {
BundleDTO bundle = testUtil.getTestBundle();
Expand All @@ -69,7 +111,7 @@ void shouldFailTaskWhenUserHasNoCaseworkerRole() throws IOException, Interrupted
String taskUrl = DOCUMENT_TASKS_ENDPOINT + "/"
+ createTaskResponse.getBody().jsonPath().getString("id");

Response taskResponse = testUtil.pollUntil(taskUrl, body -> {
Response taskResponse = testUtil.pollUntil(taskUrl, nonCaseworkerRequest, body -> {
String state = body.getString(TASK_STATE_FIELD);
return "FAILED".equals(state) || "DONE".equals(state);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,15 +379,23 @@ private BundleDocumentDTO getTestBundleDocumentWithSortIndices(String documentUr
public Response pollUntil(String endpoint,
Predicate<JsonPath> evaluator)
throws InterruptedException, IOException {
return pollUntil(endpoint, evaluator, 30);
return pollUntil(endpoint, authRequest(), evaluator, 30);
}

public Response pollUntil(String endpoint,
RequestSpecification requestSpec,
Predicate<JsonPath> evaluator)
throws InterruptedException, IOException {
return pollUntil(endpoint, requestSpec, evaluator, 30);
}

private Response pollUntil(String endpoint,
RequestSpecification requestSpec,
Predicate<JsonPath> evaluator,
int numRetries) throws InterruptedException, IOException {

for (int i = 0; i < numRetries; i++) {
Response response = authRequest().get(endpoint);
Response response = requestSpec.get(endpoint);

if (response.getStatusCode() == 500) {
throw new IOException("HTTP 500 from service");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator;
import uk.gov.hmcts.reform.em.stitching.Application;
import uk.gov.hmcts.reform.em.stitching.config.security.SecurityUtils;
import uk.gov.hmcts.reform.em.stitching.domain.Bundle;
import uk.gov.hmcts.reform.em.stitching.domain.BundleTest;
import uk.gov.hmcts.reform.em.stitching.domain.DocumentTask;
Expand All @@ -32,6 +33,7 @@

import java.io.IOException;
import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
Expand Down Expand Up @@ -73,6 +75,9 @@ class DocumentTaskResourceIntTest {
@Autowired
private OkHttpClient okHttpClient;

@MockitoBean
private SecurityUtils securityUtils;

@MockitoBean
private AuthTokenGenerator authTokenGenerator;

Expand Down Expand Up @@ -112,6 +117,7 @@ void initTest() {
defaultTestDocumentTask = createEntity();
MockInterceptor mockInterceptor = (MockInterceptor)okHttpClient.interceptors().get(0);
mockInterceptor.reset();
BDDMockito.given(securityUtils.getCurrentUserLogin()).willReturn(Optional.of("test-user"));
}

@Test
Expand Down Expand Up @@ -193,6 +199,7 @@ void createDocumentTaskWithExistingId() throws Exception {
@Test
void getDocumentTask() throws Exception {
// Initialize the database
defaultTestDocumentTask.setCreatedBy("test-user");
documentTaskRepository.saveAndFlush(defaultTestDocumentTask);

// Get the documentTask
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@

import java.time.Instant;
import java.util.List;
import java.util.Optional;

@Repository
public interface DocumentTaskRepository extends JpaRepository<DocumentTask, Long> {

Optional<DocumentTask> findByIdAndCreatedBy(Long id, String createdBy);


@Query(value =
"SELECT m.id FROM versioned_document_task m WHERE m.created_date <= :createdDate limit :numberOfRecords",
nativeQuery = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import uk.gov.hmcts.reform.em.stitching.config.security.SecurityUtils;
import uk.gov.hmcts.reform.em.stitching.domain.DocumentTask;
import uk.gov.hmcts.reform.em.stitching.info.BuildInfo;
import uk.gov.hmcts.reform.em.stitching.repository.DocumentTaskRepository;
Expand All @@ -19,17 +21,22 @@
@Service
public class DocumentTaskServiceImpl implements DocumentTaskService {

public static final String USER_NOT_FOUND = "User not found.";

private final Logger log = LoggerFactory.getLogger(DocumentTaskServiceImpl.class);
private final DocumentTaskRepository documentTaskRepository;
private final DocumentTaskMapper documentTaskMapper;
private final BuildInfo buildInfo;
private final SecurityUtils securityUtils;

public DocumentTaskServiceImpl(DocumentTaskRepository documentTaskRepository,
DocumentTaskMapper documentTaskMapper,
BuildInfo buildInfo) {
BuildInfo buildInfo,
SecurityUtils securityUtils) {
this.documentTaskRepository = documentTaskRepository;
this.documentTaskMapper = documentTaskMapper;
this.buildInfo = buildInfo;
this.securityUtils = securityUtils;
}

/**
Expand Down Expand Up @@ -61,7 +68,9 @@ public DocumentTaskDTO save(DocumentTaskDTO documentTaskDto) {
@Transactional(readOnly = true)
public Optional<DocumentTaskDTO> findOne(Long id) {
log.debug("Request to get DocumentTask : {}", id);
return documentTaskRepository.findById(id)
String currentUser = securityUtils.getCurrentUserLogin()
.orElseThrow(() -> new UsernameNotFoundException(USER_NOT_FOUND));
return documentTaskRepository.findByIdAndCreatedBy(id, currentUser)
.map(documentTaskMapper::toDto);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package uk.gov.hmcts.reform.em.stitching.service.impl;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import uk.gov.hmcts.reform.em.stitching.config.security.SecurityUtils;
import uk.gov.hmcts.reform.em.stitching.domain.DocumentTask;
import uk.gov.hmcts.reform.em.stitching.info.BuildInfo;
import uk.gov.hmcts.reform.em.stitching.repository.DocumentTaskRepository;
import uk.gov.hmcts.reform.em.stitching.service.dto.DocumentTaskDTO;
import uk.gov.hmcts.reform.em.stitching.service.mapper.DocumentTaskMapper;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class DocumentTaskServiceImplTest {

@Mock
private DocumentTaskRepository documentTaskRepository;

@Mock
private DocumentTaskMapper documentTaskMapper;

@Mock
private BuildInfo buildInfo;

@Mock
private SecurityUtils securityUtils;

@InjectMocks
private DocumentTaskServiceImpl documentTaskService;

private static final Long TASK_ID = 1L;
private static final String OWNER = "user-abc";
private static final String OTHER_USER = "user-xyz";

@Test
void findOneReturnsTaskWhenOwnedByCurrentUser() {
DocumentTask task = new DocumentTask();
DocumentTaskDTO taskDTO = new DocumentTaskDTO();
when(securityUtils.getCurrentUserLogin()).thenReturn(Optional.of(OWNER));
when(documentTaskRepository.findByIdAndCreatedBy(TASK_ID, OWNER)).thenReturn(Optional.of(task));
when(documentTaskMapper.toDto(task)).thenReturn(taskDTO);

Optional<DocumentTaskDTO> result = documentTaskService.findOne(TASK_ID);

assertTrue(result.isPresent());
assertEquals(taskDTO, result.get());
verify(documentTaskRepository).findByIdAndCreatedBy(TASK_ID, OWNER);
}

@Test
void findOneReturnsEmptyWhenTaskBelongsToDifferentUser() {
when(securityUtils.getCurrentUserLogin()).thenReturn(Optional.of(OTHER_USER));
when(documentTaskRepository.findByIdAndCreatedBy(TASK_ID, OTHER_USER)).thenReturn(Optional.empty());

Optional<DocumentTaskDTO> result = documentTaskService.findOne(TASK_ID);

assertTrue(result.isEmpty());
verify(documentTaskRepository).findByIdAndCreatedBy(TASK_ID, OTHER_USER);
}

@Test
void findOneThrowsWhenNoAuthenticatedUser() {
when(securityUtils.getCurrentUserLogin()).thenReturn(Optional.empty());

assertThrows(UsernameNotFoundException.class, () -> documentTaskService.findOne(TASK_ID));

verify(documentTaskRepository, never()).findByIdAndCreatedBy(TASK_ID, null);
}

@Test
void findOneReturnsEmptyWhenTaskDoesNotExist() {
when(securityUtils.getCurrentUserLogin()).thenReturn(Optional.of(OWNER));
when(documentTaskRepository.findByIdAndCreatedBy(TASK_ID, OWNER)).thenReturn(Optional.empty());

Optional<DocumentTaskDTO> result = documentTaskService.findOne(TASK_ID);

assertTrue(result.isEmpty());
}
}