Skip to content
This repository was archived by the owner on Sep 15, 2023. It is now read-only.

Commit

Permalink
Pad Response Time (#18)
Browse files Browse the repository at this point in the history
* refactoring: configuration of actuator and loggers

* test: “out-of-BIT” development support (#14)

* test(local): docker-compose.yml with PostgreSQL-in-a-box

- PostgreSQL data is stored in a Docker persistent volume
- SQL scripts are executed with superuser privileges at database creation time

* test(local): add Keycloak server

- Persist state into PostgreSQL

- Set up suitable initdb.d script

* test(local): Keycloak "bag-pts" realm definition

- One client in `"clients"` whose `clientId` matches the one the
Covidcode UI uses (`"ha-ui-web-client"`)

- Client allows CORS from (`webOrigins`), and redirects
to (`redirectURIs`) http://localhost:4200/ (the URL of the Covidcode
UI while in development mode)

- `"publicClient"` is set to true, meaning that the Covidcode UI doesn't
need to set up a secret on its side

- Create `bag-pts-allow` role and a `doctor` user (password `doctor`)
as a member of the role

- Ensure (via suitable `"protocolMappers"` entry) that membership in
this role appears under "userroles" at the `userinfo` OIDC
endpoint (as required by Covidcode UI:
https://github.com/admin-ch/CovidCode-UI/blob/master/src/app/auth/oauth.service.ts#L91)

- Ensure (using a second `"protocolMappers"` entry) that a `ctx` claim is set to
`"USER"`, as per README.md. (On the other hand,
the audience setting doesn't appear to be required anymore)

* test(local): rewrite URLs on port 8180 with Træfik

This lets covidcode-ui obtain its endpoints from
http://localhost:8180/.well-known/openid-configuration which is its
out-of-the-box behavior.

* test(local): `mvn exec:java`

- Add suitable stanzas to pom.xml to get the Covidcode-Service to
start up using the `local` Spring profile (i.e. fake configuration and
credentials found in `src/main/resources/application-local.yml`)

- Prepare a database for it in PostgreSQL

- Need a `haauthcodegeneration` superuser and a
`haauthcodegeneration_role_full`, as both are expected by the
Covidcode-Service SQL migration scripts

squash! [feature] `mvn exec:java`

* test: Sonarqube

Persistence is done in-container with no volumes (i.e. not very
persistent at all)

* test(local): comments in YAML on how to increase logging

* test(local): `mvn exec:java` : configure for use w/ local Keycloak

- Add new Spring profile `keycloak-local` to override
`jeap.security.oauth2.resourceserver.authorization-server`
configuration

- Apply it from `mvn exec:java` in pom.xml

* docs: development lifecycle using the new docker-compose environment

* test: avoid using port 8180

While using the docker-compose workflow, port 8180 is likely to be in
use by the local KeyCloak.

Co-authored-by: Dominique Quatravaux <dominique.quatravaux@epfl.ch>

* Update version number to 1.0.0

* Pad the response time for authcode verification calls (#16)

Co-authored-by: Fabien Cerf <fabien.cerf@bit.admin.ch>

Co-authored-by: Fabien Cerf <fabien.cerf@bit.admin.ch>
Co-authored-by: domq <dominique@quatravaux.org>
Co-authored-by: Dominique Quatravaux <dominique.quatravaux@epfl.ch>
  • Loading branch information
4 people authored Jun 22, 2020
1 parent 79d0187 commit 20d93b3
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;

@Service
@Transactional(readOnly = true)
Expand All @@ -28,23 +27,18 @@ public class AuthCodeVerificationService {
@Value("${authcodegeneration.service.callCountLimit}")
private int callCountLimit;

@Value("${authcodegeneration.service.minSleepTime}")
private int minSleepTime;

@Value("${authcodegeneration.service.maxSleepTime}")
private int maxSleepTime;

private static final Random rand = new Random();

@Transactional
public AuthorizationCodeVerifyResponseDto verify(String code, String fake) {

AuthorizationCode existingCode = authorizationCodeRepository.findByCode(code).orElse(null);
AuthorizationCode existingCode;

if (FAKE_STRING.equals(fake)) {
log.debug("Fake Call of verification !");
existingCode = AuthorizationCode.createFake();
} else {

existingCode = authorizationCodeRepository.findByCode(code).orElse(null);

if (existingCode == null) {
log.error("No AuthCode found with code '{}'", code);
throw new ResourceNotFoundException(null);
Expand All @@ -61,11 +55,6 @@ public AuthorizationCodeVerifyResponseDto verify(String code, String fake) {
try {
String token = tokenProvider.createToken(existingCode.getOnsetDate().format(DATE_FORMATTER), fake);
existingCode.incrementCallCount();

if (FAKE_STRING.equals(fake)) {
Thread.sleep((rand.nextInt(maxSleepTime) + minSleepTime));
}

return new AuthorizationCodeVerifyResponseDto(token);
} catch (Exception e) {
log.error("Error during Token Generation", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ public String createToken(String onsetDate, String fake) {
.signWith(signingKey);

builder.setExpiration(new Date(nowMillis + tokenValidity));

log.debug("Generating Custom Token");
return builder.compact();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,47 @@
import ch.admin.bag.covidcode.authcodegeneration.api.AuthorizationCodeVerifyResponseDto;
import ch.admin.bag.covidcode.authcodegeneration.service.AuthCodeVerificationService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.time.Duration;
import java.time.Instant;

@RestController
@RequestMapping("/v1/onset")
@RequiredArgsConstructor
@Slf4j
public class AuthCodeVerificationController {

private final AuthCodeVerificationService authCodeVerificationService;
private final Duration requestTime;

public AuthCodeVerificationController(AuthCodeVerificationService authCodeVerificationService, @Value("${authcodegeneration.service.requestTime}") long requestTime) {
this.authCodeVerificationService = authCodeVerificationService;
this.requestTime = Duration.ofMillis(requestTime);
}

@Operation(summary = "Authorization code verification method")
@PostMapping()
public AuthorizationCodeVerifyResponseDto verify(@Valid @RequestBody AuthorizationCodeVerificationDto verificationDto) {
var now = Instant.now().toEpochMilli();
log.debug("Call of Verify with authCode '{}'.", verificationDto.getAuthorizationCode());
return authCodeVerificationService.verify(verificationDto.getAuthorizationCode(), verificationDto.getFake());
AuthorizationCodeVerifyResponseDto responseDto = authCodeVerificationService.verify(verificationDto.getAuthorizationCode(), verificationDto.getFake());
normalizeRequestTime(now);
return responseDto;
}

private void normalizeRequestTime(long now) {
long after = Instant.now().toEpochMilli();
long duration = after - now;
try {
Thread.sleep(Math.max(requestTime.minusMillis(duration).toMillis(), 0));
} catch (Exception ex) {
log.error("Error during sleep", ex);
}
}
}
3 changes: 1 addition & 2 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,7 @@ authcodegeneration:
codeExpirationDelay: 1440
deletionCron: "0 30 1 * * ?"
onsetSubtractionDays: 2
minSleepTime: 3
maxSleepTime: 8
requestTime: 500
monitor:
prometheus:
secure: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,11 @@ class AuthCodeVerificationServiceTest {

private static final String FAKE_NOT_FAKE = "0";
private static final String FAKE_FAKE = "1";
private static final String MIN_SLEEP_TIME_KEY = "minSleepTime";
private static final String MAX_SLEEP_TIME_KEY = "maxSleepTime";
private static final String CALL_COUNT_LIMIT_KEY = "callCountLimit";
private static final String TEST_AUTHORIZATION_CODE = "123456789";
private static final String TEST_ACCESS_TOKEN = "QRMwjii77";
private static final int CODE_EXPIRATION_DELAY_IN_SECONDS = 10;
private static final int CALL_COUNT_LIMIT = 3;
private static final int SLEEP_TIME = 1;

@Mock
private AuthorizationCodeRepository repository;
Expand All @@ -53,8 +50,6 @@ void test_verify() {
//given
AuthorizationCode authCode = new AuthorizationCode(TEST_AUTHORIZATION_CODE, LocalDate.now(), LocalDate.now().minusDays(3), ZonedDateTime.now().plusSeconds(CODE_EXPIRATION_DELAY_IN_SECONDS));
ReflectionTestUtils.setField(testee, CALL_COUNT_LIMIT_KEY, CALL_COUNT_LIMIT);
ReflectionTestUtils.setField(testee, MIN_SLEEP_TIME_KEY, SLEEP_TIME);
ReflectionTestUtils.setField(testee, MAX_SLEEP_TIME_KEY, SLEEP_TIME);
when(repository.findByCode(anyString())).thenReturn(Optional.of(authCode));
when(tokenProvider.createToken(anyString(), anyString())).thenReturn(TEST_ACCESS_TOKEN);

Expand All @@ -75,8 +70,6 @@ void test_verify_with_yml_prop_callCountLimit() throws Exception {
//given
AuthorizationCode authCode = new AuthorizationCode(TEST_AUTHORIZATION_CODE, LocalDate.now(), LocalDate.now().minusDays(3), ZonedDateTime.now().plusSeconds(CODE_EXPIRATION_DELAY_IN_SECONDS));
ReflectionTestUtils.setField(testee, CALL_COUNT_LIMIT_KEY, callCountLimit);
ReflectionTestUtils.setField(testee, MIN_SLEEP_TIME_KEY, SLEEP_TIME);
ReflectionTestUtils.setField(testee, MAX_SLEEP_TIME_KEY, SLEEP_TIME);
when(repository.findByCode(anyString())).thenReturn(Optional.of(authCode));
when(tokenProvider.createToken(anyString(), anyString())).thenReturn(TEST_ACCESS_TOKEN);

Expand All @@ -92,8 +85,6 @@ void test_verify_token_onset_date_is_equal_original_minus_3_days() {
//given
AuthorizationCode authCode = new AuthorizationCode(TEST_AUTHORIZATION_CODE, LocalDate.now(), LocalDate.now().minusDays(3), ZonedDateTime.now().plusSeconds(CODE_EXPIRATION_DELAY_IN_SECONDS));
ReflectionTestUtils.setField(testee, CALL_COUNT_LIMIT_KEY, CALL_COUNT_LIMIT);
ReflectionTestUtils.setField(testee, MIN_SLEEP_TIME_KEY, SLEEP_TIME);
ReflectionTestUtils.setField(testee, MAX_SLEEP_TIME_KEY, SLEEP_TIME);
when(repository.findByCode(anyString())).thenReturn(Optional.of(authCode));
when(tokenProvider.createToken(anyString(), anyString())).thenReturn(TEST_ACCESS_TOKEN);

Expand All @@ -110,8 +101,6 @@ void test_verify_call_count_reached() {
//given
AuthorizationCode authCode = new AuthorizationCode(TEST_AUTHORIZATION_CODE, LocalDate.now(), LocalDate.now().minusDays(3), ZonedDateTime.now().plusSeconds(CODE_EXPIRATION_DELAY_IN_SECONDS));
ReflectionTestUtils.setField(testee, CALL_COUNT_LIMIT_KEY, CALL_COUNT_LIMIT);
ReflectionTestUtils.setField(testee, MIN_SLEEP_TIME_KEY, SLEEP_TIME);
ReflectionTestUtils.setField(testee, MAX_SLEEP_TIME_KEY, SLEEP_TIME);
when(repository.findByCode(anyString())).thenReturn(Optional.of(authCode));
when(tokenProvider.createToken(anyString(), anyString())).thenReturn(TEST_ACCESS_TOKEN);

Expand All @@ -128,8 +117,6 @@ void test_verify_call_count_reached() {
void test_verify_call_fake_count_never_reached() {
//given
ReflectionTestUtils.setField(testee, CALL_COUNT_LIMIT_KEY, CALL_COUNT_LIMIT);
ReflectionTestUtils.setField(testee, MIN_SLEEP_TIME_KEY, SLEEP_TIME);
ReflectionTestUtils.setField(testee, MAX_SLEEP_TIME_KEY, SLEEP_TIME);
when(tokenProvider.createToken(anyString(), anyString())).thenReturn(TEST_ACCESS_TOKEN);

//when
Expand Down Expand Up @@ -168,8 +155,6 @@ void test_verify_exception_token_provider() {
AuthorizationCode authCode = new AuthorizationCode(TEST_AUTHORIZATION_CODE, LocalDate.now(), LocalDate.now(), ZonedDateTime.now().plusDays(1));
when(repository.findByCode(anyString())).thenReturn(Optional.of(authCode));
ReflectionTestUtils.setField(testee, CALL_COUNT_LIMIT_KEY, CALL_COUNT_LIMIT);
ReflectionTestUtils.setField(testee, MIN_SLEEP_TIME_KEY, SLEEP_TIME);
ReflectionTestUtils.setField(testee, MAX_SLEEP_TIME_KEY, SLEEP_TIME);
when(tokenProvider.createToken(anyString(), anyString())).thenThrow(new NullPointerException());

//when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,13 @@ class AuthCodeVerificationControllerTest {

@Mock
private AuthCodeVerificationService authCodeVerificationService;
@InjectMocks
private AuthCodeVerificationController controller;

private MockMvc mockMvc;
private ObjectMapper mapper = new ObjectMapper();

@BeforeEach
void setup() {
this.mockMvc = standaloneSetup(controller).build();
this.mockMvc = standaloneSetup(new AuthCodeVerificationController(authCodeVerificationService, 0)).build();
}

@Test
Expand Down

0 comments on commit 20d93b3

Please sign in to comment.