diff --git a/.aws/task-definition-template.json b/.aws/task-definition-template.json index a924197e..689e5923 100644 --- a/.aws/task-definition-template.json +++ b/.aws/task-definition-template.json @@ -4,6 +4,10 @@ "name": "tis-trainee-credentials", "image": "430723991443.dkr.ecr.eu-west-2.amazonaws.com/tis-trainee-credentials:latest", "secrets": [ + { + "name": "AWS_XRAY_DAEMON_ADDRESS", + "valueFrom": "/tis/monitoring/xray/daemon-host" + }, { "name": "DB_HOST", "valueFrom": "/tis/trainee/${environment}/db/host" @@ -116,7 +120,7 @@ "value": "eu-west-2" }, { - "name": "SENTRY_ENVIRONMENT", + "name": "ENVIRONMENT", "value": "${environment}" } ] diff --git a/README.md b/README.md index c343c106..d5b4eb77 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # TIS Trainee Credentials ## About + This service issues and verifies trainee digital credentials. ## Developing @@ -17,11 +18,13 @@ gradlew bootRun | Name | Description | Default | |-----------------------------------|-------------------------------------------------------------------------------------------------------------------------|-------------| +| AWS_XRAY_DAEMON_ADDRESS | The AWS XRay daemon host. | | | DB_HOST | The MongoDB host to connect to. | localhost | | DB_PORT | The port to connect to MongoDB on. | 27017 | | DB_NAME | The name of the MongoDB database. | credentials | | DB_USER | The username to access the MongoDB instance. | admin | | DB_PASSWORD | The password to access the MongoDB instance. | pwd | +| ENVIRONMENT | The environment to log events against. | local | | GATEWAY_HOST | The credential gateway host. | | | GATEWAY_CLIENT_ID | The client ID for the credential gateway. | | | GATEWAY_CLIENT_SECRET | The client secret for the credential gateway. | | @@ -29,7 +32,6 @@ gradlew bootRun | GATEWAY_ISSUING_REDIRECT_URI | Where the gateway issue should redirect to after issuing. | | | GATEWAY_VERIFICATION_REDIRECT_URI | Where the gateway issue should redirect to verify a supplied credential, e.g. `/api/credentials/verify/callback`. | | | SENTRY_DSN | A Sentry error monitoring Data Source Name. | | -| SENTRY_ENVIRONMENT | The environment to log Sentry events against. | local | | SIGNATURE_SECRET_KEY | The secret key used to validate signed data. | | #### Usage Examples diff --git a/build.gradle b/build.gradle index ca9c447c..126b94cd 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ plugins { } group = "uk.nhs.tis.trainee" -version = "0.16.0" +version = "0.17.0" configurations { compileOnly { @@ -51,6 +51,9 @@ dependencies { implementation "io.awspring.cloud:spring-cloud-starter-aws" implementation "io.awspring.cloud:spring-cloud-aws-messaging" + //AWS X-ray + implementation "com.amazonaws:aws-xray-recorder-sdk-spring:2.14.0" + implementation "commons-codec:commons-codec:1.16.0" ext.jjwtVersion = "0.11.5" diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/IssueResource.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/IssueResource.java index 3c960434..725ed0b6 100644 --- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/IssueResource.java +++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/IssueResource.java @@ -21,6 +21,7 @@ package uk.nhs.hee.tis.trainee.credentials.api; +import com.amazonaws.xray.spring.aop.XRayEnabled; import java.net.URI; import java.util.Optional; import java.util.UUID; @@ -50,6 +51,7 @@ @Slf4j @RestController() @RequestMapping("/api/issue") +@XRayEnabled public class IssueResource { private final IssuanceService service; diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java index 99a4d894..672b3852 100644 --- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java +++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java @@ -21,6 +21,7 @@ package uk.nhs.hee.tis.trainee.credentials.api; +import com.amazonaws.xray.spring.aop.XRayEnabled; import jakarta.validation.Valid; import java.net.URI; import lombok.extern.slf4j.Slf4j; @@ -46,6 +47,7 @@ @RestController @RequestMapping("/api/verify") @Validated +@XRayEnabled public class VerifyResource { private final VerificationService service; diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java new file mode 100644 index 00000000..bc76dc95 --- /dev/null +++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java @@ -0,0 +1,72 @@ +/* + * The MIT License (MIT) + * + * Copyright 2023 Crown Copyright (Health Education England) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package uk.nhs.hee.tis.trainee.credentials.config; + +import com.amazonaws.xray.entities.Subsegment; +import com.amazonaws.xray.spring.aop.AbstractXRayInterceptor; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Map; +import java.util.Optional; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; +import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata; + +/** + * Extend the Xray interceptor with instructions to wrap annotated Resource and Service beans. + */ +@Aspect +@Component +@ConditionalOnExpression("!T(org.springframework.util.StringUtils)" + + ".isEmpty('${com.amazonaws.xray.emitters.daemon-address}')") +public class AwsXrayInterceptor extends AbstractXRayInterceptor { + + private final EcsMetadata ecsMetadata; + private final ObjectMapper mapper; + + AwsXrayInterceptor(Optional ecsMetadata, ObjectMapper mapper) { + this.ecsMetadata = ecsMetadata.orElse(null); + this.mapper = mapper; + } + + @Override + protected Map> generateMetadata(ProceedingJoinPoint pjp, + Subsegment subsegment) { + Map> metadata = super.generateMetadata(pjp, subsegment); + + Map taskMetadataMap = mapper.convertValue(ecsMetadata, new TypeReference<>() { + }); + metadata.put("EcsMetadata", taskMetadataMap); + + return metadata; + } + + @Override + @Pointcut( + "@within(com.amazonaws.xray.spring.aop.XRayEnabled) && (bean(*Resource) || bean(*Service))") + public void xrayEnabledClasses() { + + } +} diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/EcsMetadataConfiguration.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/EcsMetadataConfiguration.java index 53c4b5f3..8746aefc 100644 --- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/EcsMetadataConfiguration.java +++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/EcsMetadataConfiguration.java @@ -30,6 +30,9 @@ import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata.ContainerMetadata; import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata.TaskMetadata; +/** + * Configuration for retrieval of ECS metadata. + */ @Configuration @ConditionalOnExpression("!T(org.springframework.util.StringUtils)" + ".isEmpty('${ecs.container.metadata.uri.v4:}')") @@ -53,6 +56,12 @@ public EcsMetadata ecsMetadata(RestTemplate restTemplate, return new EcsMetadata(taskMetadata, containerMetadata); } + /** + * A representation of ECS metadata. + * + * @param taskMetadata The ECS task metadata. + * @param containerMetadata The ECS container metadata. + */ public record EcsMetadata( @JsonProperty("TaskMetadata") TaskMetadata taskMetadata, @@ -60,6 +69,14 @@ public record EcsMetadata( @JsonProperty("ContainerMetadata") ContainerMetadata containerMetadata) { + /** + * A representation of ECS task metadata. + * + * @param cluster The ECS cluster. + * @param taskArn The running task's ARN. + * @param family The task definition family. + * @param revision The task definition revision. + */ record TaskMetadata( @JsonProperty("Cluster") String cluster, @@ -75,6 +92,12 @@ record TaskMetadata( } + /** + * A representation of ECS container metadata. + * + * @param containerArn The ECS container ARN. + * @param logOptions The container's log options. + */ record ContainerMetadata( @JsonProperty("ContainerARN") @@ -83,6 +106,13 @@ record ContainerMetadata( @JsonProperty("LogOptions") LogOptions logOptions) { + /** + * A representation of ECS container log options. + * + * @param logGroup The container's log group. + * @param region The container's log region. + * @param logStream The container's current log stream. + */ record LogOptions( @JsonProperty("awslogs-group") String logGroup, diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfiguration.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfiguration.java index 69c85c54..4eb3b641 100644 --- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfiguration.java +++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfiguration.java @@ -21,9 +21,13 @@ package uk.nhs.hee.tis.trainee.credentials.filter; +import com.amazonaws.xray.jakarta.servlet.AWSXRayServletFilter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; /** * A configuration for request filters. @@ -31,6 +35,23 @@ @Configuration public class FilterConfiguration { + /** + * Configure the AWS Xray filter with the segment name set to service and environment. + * + * @param environment The environment to use. + * @return The {@link AWSXRayServletFilter} for the registration. + */ + @Bean + @Order(0) + @ConditionalOnExpression("!T(org.springframework.util.StringUtils)" + + ".isEmpty('${com.amazonaws.xray.emitters.daemon-address}')") + public FilterRegistrationBean registerTracingFilter( + @Value("${application.environment}") String environment) { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new AWSXRayServletFilter("tis-trainee-credentials-" + environment)); + return registrationBean; + } + /** * Register a {@link SignedDataFilter}. * @@ -38,6 +59,7 @@ public class FilterConfiguration { * @return The {@link FilterRegistrationBean} for the registration. */ @Bean + @Order(1) public FilterRegistrationBean registerSignedDataFilter( SignedDataFilter filter) { FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); @@ -55,6 +77,7 @@ public FilterRegistrationBean registerSignedDataFilter( * @return The {@link FilterRegistrationBean} for the registration. */ @Bean + @Order(1) public FilterRegistrationBean registerVerifiedSessionFilter( VerifiedSessionFilter filter) { FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/GatewayService.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/GatewayService.java index 5bd9e267..272825f6 100644 --- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/GatewayService.java +++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/GatewayService.java @@ -21,6 +21,7 @@ package uk.nhs.hee.tis.trainee.credentials.service; +import com.amazonaws.xray.spring.aop.XRayEnabled; import com.fasterxml.jackson.annotation.JsonProperty; import io.jsonwebtoken.Claims; import io.jsonwebtoken.impl.DefaultClaims; @@ -50,6 +51,7 @@ */ @Slf4j @Service +@XRayEnabled public class GatewayService { private static final String CLIENT_ID = "client_id"; diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/IssuanceService.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/IssuanceService.java index 4f0da24b..e58fed52 100644 --- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/IssuanceService.java +++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/IssuanceService.java @@ -21,6 +21,7 @@ package uk.nhs.hee.tis.trainee.credentials.service; +import com.amazonaws.xray.spring.aop.XRayEnabled; import io.jsonwebtoken.Claims; import java.net.URI; import java.time.Instant; @@ -45,6 +46,7 @@ */ @Slf4j @Service +@XRayEnabled public class IssuanceService { private final GatewayService gatewayService; diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/JwtService.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/JwtService.java index 684b1342..11459d43 100644 --- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/JwtService.java +++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/JwtService.java @@ -21,6 +21,7 @@ package uk.nhs.hee.tis.trainee.credentials.service; +import com.amazonaws.xray.spring.aop.XRayEnabled; import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtParser; @@ -41,6 +42,7 @@ */ @Slf4j @Service +@XRayEnabled public class JwtService { private final ObjectMapper mapper; diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/RevocationService.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/RevocationService.java index 88e6b2f7..a1a3779d 100644 --- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/RevocationService.java +++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/RevocationService.java @@ -21,6 +21,7 @@ package uk.nhs.hee.tis.trainee.credentials.service; +import com.amazonaws.xray.spring.aop.XRayEnabled; import java.time.Instant; import java.util.List; import java.util.Optional; @@ -39,6 +40,7 @@ */ @Slf4j @Service +@XRayEnabled public class RevocationService { private final CredentialMetadataRepository credentialMetadataRepository; diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/VerificationService.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/VerificationService.java index a72fb0af..d106498b 100644 --- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/VerificationService.java +++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/VerificationService.java @@ -21,6 +21,7 @@ package uk.nhs.hee.tis.trainee.credentials.service; +import com.amazonaws.xray.spring.aop.XRayEnabled; import io.jsonwebtoken.Claims; import jakarta.servlet.http.HttpServletRequest; import java.net.URI; @@ -45,6 +46,7 @@ */ @Slf4j @Service +@XRayEnabled public class VerificationService { private static final String CREDENTIAL_PREFIX = "openid "; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 09e427dc..e24430da 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,6 +16,7 @@ application: verification-request: ${REDIS_TTL_VERIFICATION_REQUEST:300} verified-session: ${REDIS_TTL_VERIFIED_SESSION:600} credential-metadata: ${REDIS_TTL_ISSUING_FLOW:600} + environment: ${ENVIRONMENT:local} gateway: host: https://${GATEWAY_HOST} organisation-id: ${GATEWAY_ORGANISATION_ID} @@ -41,9 +42,15 @@ application: signature: secret-key: ${SIGNATURE_SECRET_KEY} +com: + amazonaws: + xray: + emitters: + daemon-address: ${AWS_XRAY_DAEMON_ADDRESS:} + sentry: dsn: ${SENTRY_DSN:} - environment: ${SENTRY_ENVIRONMENT:} + environment: ${application.environment} spring: data: diff --git a/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptorTest.java b/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptorTest.java new file mode 100644 index 00000000..31a850b2 --- /dev/null +++ b/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptorTest.java @@ -0,0 +1,103 @@ +/* + * The MIT License (MIT) + * + * Copyright 2023 Crown Copyright (Health Education England) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package uk.nhs.hee.tis.trainee.credentials.config; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.amazonaws.xray.entities.Subsegment; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Map; +import java.util.Optional; +import org.aspectj.lang.ProceedingJoinPoint; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata; +import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata.ContainerMetadata; +import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata.ContainerMetadata.LogOptions; +import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata.TaskMetadata; + +class AwsXrayInterceptorTest { + + private ObjectMapper objectMapper; + private ProceedingJoinPoint pjp; + private Subsegment subsegment; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + subsegment = mock(Subsegment.class); + + pjp = mock(ProceedingJoinPoint.class); + when(pjp.getTarget()).thenReturn(Object.class); + } + + @Test + void shouldGenerateNullEcsMetadataWhenNotAvailable() { + AwsXrayInterceptor interceptor = new AwsXrayInterceptor(Optional.empty(), objectMapper); + + Map> metadata = interceptor.generateMetadata(pjp, subsegment); + + assertThat("Unexpected X-Ray metadata.", metadata, notNullValue()); + + Map ecsMetadataMap = metadata.get("EcsMetadata"); + assertThat("Unexpected ECS metadata.", ecsMetadataMap, nullValue()); + } + + @Test + void shouldGenerateEcsMetadataWhenAvailable() { + EcsMetadata ecsMetadata = new EcsMetadata( + new TaskMetadata("cluster", "taskArn", "family", "revision"), + new ContainerMetadata("containerArn", new LogOptions("logGroup", "region", "logStream"))); + AwsXrayInterceptor interceptor = new AwsXrayInterceptor(Optional.of(ecsMetadata), objectMapper); + + Map> metadata = interceptor.generateMetadata(pjp, subsegment); + + assertThat("Unexpected X-Ray metadata.", metadata, notNullValue()); + + Map ecsMetadataMap = metadata.get("EcsMetadata"); + assertThat("Unexpected ECS metadata.", ecsMetadataMap, notNullValue()); + + Map taskMetadataMap = (Map) ecsMetadataMap.get("TaskMetadata"); + assertThat("Unexpected task metadata.", taskMetadataMap, notNullValue()); + assertThat("Unexpected cluster.", taskMetadataMap.get("Cluster"), is("cluster")); + assertThat("Unexpected task ARN.", taskMetadataMap.get("TaskARN"), is("taskArn")); + assertThat("Unexpected family.", taskMetadataMap.get("Family"), is("family")); + + Map containerMetadataMap = (Map) ecsMetadataMap.get( + "ContainerMetadata"); + assertThat("Unexpected container metadata.", containerMetadataMap, notNullValue()); + assertThat("Unexpected container ARN.", containerMetadataMap.get("ContainerARN"), + is("containerArn")); + + Map logOptionsMap = (Map) containerMetadataMap.get( + "LogOptions"); + assertThat("Unexpected log options metadata.", logOptionsMap, notNullValue()); + assertThat("Unexpected log group.", logOptionsMap.get("awslogs-group"), is("logGroup")); + assertThat("Unexpected log region.", logOptionsMap.get("awslogs-region"), is("region")); + assertThat("Unexpected log stream.", logOptionsMap.get("awslogs-stream"), is("logStream")); + } +} diff --git a/src/test/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfigurationTest.java b/src/test/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfigurationTest.java index 067edf91..c86cfb83 100644 --- a/src/test/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfigurationTest.java +++ b/src/test/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfigurationTest.java @@ -22,19 +22,28 @@ package uk.nhs.hee.tis.trainee.credentials.filter; import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.Mockito.mock; +import com.amazonaws.xray.jakarta.servlet.AWSXRayServletFilter; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Collection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import uk.nhs.hee.tis.trainee.credentials.service.VerificationService; class FilterConfigurationTest { + private static final String DAEMON_PROPERTY = "com.amazonaws.xray.emitters.daemon-address"; + private static final String SIGNATURE_SECRET_KEY = "test-secret-key"; FilterConfiguration configuration; @@ -44,6 +53,43 @@ void setUp() { configuration = new FilterConfiguration(); } + @Test + void shouldNotRegisterTracingFilterWhenDaemonAddressNotSet() { + ApplicationContextRunner runner = new ApplicationContextRunner() + .withUserConfiguration(FilterConfiguration.class) + .withBean(SignedDataFilter.class, () -> new SignedDataFilter(null, null, null)) + .withBean(VerifiedSessionFilter.class, () -> new VerifiedSessionFilter(null)); + + runner + .withPropertyValues(DAEMON_PROPERTY + "=") + .run(context -> assertThat("Unexpected bean presence.", + context.containsBean("registerTracingFilter"), is(false))); + } + + @Test + void shouldRegisterTracingFilterWhenDaemonAddressSet() { + ApplicationContextRunner runner = new ApplicationContextRunner() + .withUserConfiguration(FilterConfiguration.class) + .withBean(SignedDataFilter.class, () -> new SignedDataFilter(null, null, null)) + .withBean(VerifiedSessionFilter.class, () -> new VerifiedSessionFilter(null)); + + runner + .withPropertyValues(DAEMON_PROPERTY + "=https://localhost:1234") + .run(context -> assertAll( + () -> assertThat("Unexpected bean presence.", + context.containsBean("registerTracingFilter"), is(true)), + () -> assertThat("Unexpected bean type.", context.getBean("registerTracingFilter"), + instanceOf(FilterRegistrationBean.class)) + )); + } + + @Test + void shouldRegisterTracingFilter() { + var registrationBean = configuration.registerTracingFilter("test"); + AWSXRayServletFilter registeredFilter = registrationBean.getFilter(); + assertThat("Unexpected registered filter.", registeredFilter, notNullValue()); + } + @Test void shouldRegisterSignedDataFilter() { SignedDataFilter filter = new SignedDataFilter(new ObjectMapper(), SIGNATURE_SECRET_KEY, null); diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index be0b182e..9cc7b910 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -5,5 +5,6 @@ application: verification-request: 60 verified-session: 60 credential-metadata: 60 + environment: local signature: secret-key: test-secret-key