Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 90db8eaf authored by Julia StopCovid's avatar Julia StopCovid
Browse files

Merge branch 'feat-enable-token-validation' into 'feat-tests-integration'

Feat enable token validation

See merge request !27
parents 23bb86b9 0a8d8a5a
No related branches found
No related tags found
2 merge requests!30feat: Tests d'integration,!27Feat enable token validation
Showing with 581 additions and 357 deletions
......@@ -93,6 +93,12 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
......
package fr.gouv.stopc.robertserver.ws.controller.impl;
import java.util.Optional;
import javax.inject.Inject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import fr.gouv.stopc.robertserver.ws.controller.IReportController;
import fr.gouv.stopc.robertserver.ws.dto.ReportBatchResponseDto;
import fr.gouv.stopc.robertserver.ws.dto.VerifyResponseDto;
import fr.gouv.stopc.robertserver.ws.exception.RobertServerBadRequestException;
import fr.gouv.stopc.robertserver.ws.exception.RobertServerException;
import fr.gouv.stopc.robertserver.ws.exception.RobertServerUnauthorizedException;
import fr.gouv.stopc.robertserver.ws.service.ContactDtoService;
import fr.gouv.stopc.robertserver.ws.service.IRestApiService;
import fr.gouv.stopc.robertserver.ws.utils.MessageConstants;
import fr.gouv.stopc.robertserver.ws.vo.ReportBatchRequestVo;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
public class ReportControllerImpl implements IReportController {
private ContactDtoService contactDtoService;
private RestTemplate restTemplate;
@Value("${submission.code.server.host}")
private String serverCodeHost;
@Value("${submission.code.server.port}")
private String serverCodePort;
@Value("${submission.code.server.verify.path}")
private String serverCodeVerificationUri;
@Inject
public ReportControllerImpl(ContactDtoService contactDtoService, RestTemplate restTemplate) {
this.contactDtoService = contactDtoService;
this.restTemplate = restTemplate;
}
private boolean areBothFieldsPresent(ReportBatchRequestVo reportBatchRequestVo) {
return !CollectionUtils.isEmpty(reportBatchRequestVo.getContacts())
&& StringUtils.isNotEmpty(reportBatchRequestVo.getContactsAsBinary());
}
private boolean areBothFieldsAbsent(ReportBatchRequestVo reportBatchRequestVo) {
return CollectionUtils.isEmpty(reportBatchRequestVo.getContacts())
&& StringUtils.isEmpty(reportBatchRequestVo.getContactsAsBinary());
}
@Override
public ResponseEntity<ReportBatchResponseDto> reportContactHistory(ReportBatchRequestVo reportBatchRequestVo) throws RobertServerException {
if (CollectionUtils.isEmpty(reportBatchRequestVo.getContacts())) {
log.warn("No contacts in request");
return ResponseEntity.badRequest().build();
}
if (areBothFieldsPresent(reportBatchRequestVo)) {
log.warn("Contacts and ContactsAsBinary are both present");
return ResponseEntity.badRequest().build();
} else if (areBothFieldsAbsent(reportBatchRequestVo)) {
log.warn("Contacts and ContactsAsBinary are absent");
return ResponseEntity.badRequest().build();
}
checkValidityToken(reportBatchRequestVo.getToken());
contactDtoService.saveContacts(reportBatchRequestVo.getContacts());
ReportBatchResponseDto reportBatchResponseDto = ReportBatchResponseDto.builder().message(MessageConstants.SUCCESSFUL_OPERATION.getValue()).success(Boolean.TRUE).build();
return ResponseEntity.ok(reportBatchResponseDto);
}
private final ContactDtoService contactDtoService;
private void checkValidityToken(String token) throws RobertServerException {
private final IRestApiService restApiService;
if (StringUtils.isEmpty(token)) {
log.warn("No token provided");
throw new RobertServerBadRequestException(MessageConstants.INVALID_DATA.getValue());
}
if (token.length() != 6 && token.length() != 36) {
log.warn("Token size is incorrect");
throw new RobertServerBadRequestException(MessageConstants.INVALID_DATA.getValue());
}
// TODO: Enable this when the token validation service is available
// ResponseEntity<VerifyResponseDto> response = restTemplate.getForEntity(constructUri(), VerifyResponseDto.class, initHttpEntity(token));
@Inject
public ReportControllerImpl(final ContactDtoService contactDtoService, final IRestApiService restApiService) {
// boolean isValid = Optional.ofNullable(response).map(ResponseEntity::getBody).map(VerifyResponseDto::isValid).orElse(false);
this.contactDtoService = contactDtoService;
this.restApiService = restApiService;
}
// TODO: If isValid == false, then throw exception (when token validation is enabled).
if (false) {
throw new RobertServerUnauthorizedException(MessageConstants.INVALID_AUTHENTICATION.getValue());
}
}
private boolean areBothFieldsPresent(ReportBatchRequestVo reportBatchRequestVo) {
return !CollectionUtils.isEmpty(reportBatchRequestVo.getContacts())
&& StringUtils.isNotEmpty(reportBatchRequestVo.getContactsAsBinary());
}
private String getCodeType(String token) {
private boolean areBothFieldsAbsent(ReportBatchRequestVo reportBatchRequestVo) {
return CollectionUtils.isEmpty(reportBatchRequestVo.getContacts())
&& StringUtils.isEmpty(reportBatchRequestVo.getContactsAsBinary());
}
return token.length() == 6 ? "6-alphanum" : "UUIDv4";
}
@Override
public ResponseEntity<ReportBatchResponseDto> reportContactHistory(ReportBatchRequestVo reportBatchRequestVo) throws RobertServerException {
private HttpEntity<VerifyRequestVo> initHttpEntity(String token) {
if (CollectionUtils.isEmpty(reportBatchRequestVo.getContacts())) {
log.warn("No contacts in request");
return ResponseEntity.badRequest().build();
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
if (areBothFieldsPresent(reportBatchRequestVo)) {
log.warn("Contacts and ContactsAsBinary are both present");
return ResponseEntity.badRequest().build();
} else if (areBothFieldsAbsent(reportBatchRequestVo)) {
log.warn("Contacts and ContactsAsBinary are absent");
return ResponseEntity.badRequest().build();
}
return new HttpEntity(new VerifyRequestVo(token, getCodeType(token)), headers);
}
checkValidityToken(reportBatchRequestVo.getToken());
private String constructUri() {
contactDtoService.saveContacts(reportBatchRequestVo.getContacts());
return UriComponentsBuilder.newInstance().scheme("http").host(serverCodeHost).port(serverCodePort).path(serverCodeVerificationUri).build().toString();
}
ReportBatchResponseDto reportBatchResponseDto = ReportBatchResponseDto.builder().message(MessageConstants.SUCCESSFUL_OPERATION.getValue()).success(Boolean.TRUE).build();
return ResponseEntity.ok(reportBatchResponseDto);
}
@NoArgsConstructor
@AllArgsConstructor
@Data
class VerifyRequestVo {
private void checkValidityToken(String token) throws RobertServerException {
private String code;
if (StringUtils.isEmpty(token)) {
log.warn("No token provided");
throw new RobertServerBadRequestException(MessageConstants.INVALID_DATA.getValue());
}
private String type;
if (token.length() != 6 && token.length() != 36) {
log.warn("Token size is incorrect");
throw new RobertServerBadRequestException(MessageConstants.INVALID_DATA.getValue());
}
}
Optional<VerifyResponseDto> response = this.restApiService.verifyReportToken(token, getCodeType(token));
@NoArgsConstructor
@AllArgsConstructor
@Data
class VerifyResponseDto {
if (!response.isPresent() || !response.get().isValid()) {
log.warn("Verifying the token failed");
throw new RobertServerUnauthorizedException(MessageConstants.INVALID_AUTHENTICATION.getValue());
}
private boolean valid;
log.info("Verifying the token succeeded");
}
}
private String getCodeType(String token) {
return token.length() == 6 ? "2" : "1";
}
}
package fr.gouv.stopc.robertserver.ws.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class VerifyResponseDto {
private boolean valid;
}
package fr.gouv.stopc.robertserver.ws.service;
import java.util.Optional;
import fr.gouv.stopc.robertserver.ws.dto.VerifyResponseDto;
public interface IRestApiService {
Optional<VerifyResponseDto> verifyReportToken(String token, String type);
}
package fr.gouv.stopc.robertserver.ws.service.impl;
import java.net.URI;
import java.util.Optional;
import javax.inject.Inject;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import fr.gouv.stopc.robertserver.ws.dto.VerifyResponseDto;
import fr.gouv.stopc.robertserver.ws.service.IRestApiService;
import fr.gouv.stopc.robertserver.ws.utils.PropertyLoader;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class RestApiServiceImpl implements IRestApiService {
private final PropertyLoader propertyLoader;
private final RestTemplate restTemplate;
@Inject
public RestApiServiceImpl(final PropertyLoader propertyLoader, final RestTemplate restTemplate) {
this.propertyLoader = propertyLoader;
this.restTemplate = restTemplate;
}
@Override
public Optional<VerifyResponseDto> verifyReportToken(String token, String type) {
if(StringUtils.isEmpty(token) || StringUtils.isEmpty(type)) {
return Optional.empty();
}
try {
ResponseEntity<VerifyResponseDto> response = restTemplate.getForEntity(buildReportTokenVerificationURI(token, type),
VerifyResponseDto.class);
return Optional.ofNullable(response.getBody());
} catch (RestClientException e) {
log.error("Unable to verify the token due to {}", e.getMessage());
}
return Optional.empty();
}
private URI buildReportTokenVerificationURI(String token, String type) {
return UriComponentsBuilder.newInstance().scheme("http")
.host(this.propertyLoader.getServerCodeHost())
.port(this.propertyLoader.getServerCodePort())
.path(this.propertyLoader.getServerCodeVerificationPath())
.queryParam("code", token)
.queryParam("type", type)
.build()
.encode()
.toUri();
}
}
......@@ -9,31 +9,40 @@ import lombok.Getter;
@Component
public class PropertyLoader {
@Value("${robert.crypto.server.host}")
private String cryptoServerHost;
@Value("${robert.crypto.server.port}")
private String cryptoServerPort;
/**
*
* @return the verification URL for the captcha
*/
@Value("${captcha.verify.url}")
private String captchaVerificationUrl;
/**
*
* @return the secret to be sent to the captcha server along with challenge response
*/
@Value("${captcha.secret}")
private String captchaSecret;
/**
*
* @return the hostname of the site to check against the response from the captcha server
*/
@Value("${captcha.hostname}")
private String captchaHostname;
@Value("${robert.crypto.server.host}")
private String cryptoServerHost;
@Value("${robert.crypto.server.port}")
private String cryptoServerPort;
/**
*
* @return the verification URL for the captcha
*/
@Value("${captcha.verify.url}")
private String captchaVerificationUrl;
/**
*
* @return the secret to be sent to the captcha server along with challenge response
*/
@Value("${captcha.secret}")
private String captchaSecret;
/**
*
* @return the hostname of the site to check against the response from the captcha server
*/
@Value("${captcha.hostname}")
private String captchaHostname;
@Value("${submission.code.server.host}")
private String serverCodeHost;
@Value("${submission.code.server.port}")
private String serverCodePort;
@Value("${submission.code.server.verify.path}")
private String serverCodeVerificationPath;
}
package test.fr.gouv.stopc.robertserver.ws.service.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
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;
import static org.mockito.ArgumentMatchers.any;
import java.net.URI;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import fr.gouv.stopc.robertserver.ws.dto.VerifyResponseDto;
import fr.gouv.stopc.robertserver.ws.service.impl.RestApiServiceImpl;
import fr.gouv.stopc.robertserver.ws.utils.PropertyLoader;
@ExtendWith(SpringExtension.class)
public class RestApiServiceImplTest {
@InjectMocks
private RestApiServiceImpl restApiServiceImpl;
@Mock
private RestTemplate restTemplate;
@Mock
private PropertyLoader propertyLoader;
@BeforeEach
public void beforeEach() {
assertNotNull(restApiServiceImpl);
assertNotNull(restTemplate);
assertNotNull(propertyLoader);
when(this.propertyLoader.getServerCodeHost()).thenReturn("localhost");
when(this.propertyLoader.getServerCodePort()).thenReturn("8080");
when(this.propertyLoader.getServerCodeVerificationPath()).thenReturn("/api/v1/verify");
}
@Test
public void testVerifyReportTokenWhenTokenIsNullFails() {
// When
Optional<VerifyResponseDto> response = this.restApiServiceImpl.verifyReportToken(null, "notEmpty");
// Then
assertFalse(response.isPresent());
verify(this.restTemplate, never()).getForEntity(any(URI.class), any(Class.class));
}
@Test
public void testVerifyReportTokenWhenTokenIsEmptyFails() {
// When
Optional<VerifyResponseDto> response = this.restApiServiceImpl.verifyReportToken("", "notEmpty");
// Then
assertFalse(response.isPresent());
verify(this.restTemplate, never()).getForEntity(any(URI.class), any(Class.class));
}
@Test
public void testVerifyReportTokenWhenTypeIsNullFails() {
// When
Optional<VerifyResponseDto> response = this.restApiServiceImpl.verifyReportToken("token", null);
// Then
assertFalse(response.isPresent());
verify(this.restTemplate, never()).getForEntity(any(URI.class), any(Class.class));
}
@Test
public void testVerifyReportTokenWhenTypeIsEmptyFails() {
// When
Optional<VerifyResponseDto> response = this.restApiServiceImpl.verifyReportToken("token", "");
// Then
assertFalse(response.isPresent());
verify(this.restTemplate, never()).getForEntity(any(URI.class), any(Class.class));
}
@Test
public void testVerifyReportTokenAnExceptionIsThrownFails() {
// Given
when(this.restTemplate.getForEntity(any(URI.class), any(Class.class))).thenThrow(
new HttpClientErrorException(HttpStatus.BAD_REQUEST));
// When
Optional<VerifyResponseDto> response = this.restApiServiceImpl.verifyReportToken("token", "type");
// Then
assertFalse(response.isPresent());
verify(this.restTemplate).getForEntity(any(URI.class), any(Class.class));
}
@Test
public void testVerifyReportTokenShouldSucceed() {
// Given
VerifyResponseDto verified = VerifyResponseDto.builder().valid(true).build();
when(this.restTemplate.getForEntity(any(URI.class), any(Class.class))).thenReturn(ResponseEntity.ok(verified));
// When
Optional<VerifyResponseDto> response = this.restApiServiceImpl.verifyReportToken("token", "type");
// Then
assertTrue(response.isPresent());
assertEquals(verified, response.get());
verify(this.restTemplate).getForEntity(any(URI.class), any(Class.class));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment