Skip to content

Commit

Permalink
Merge pull request #44 from Health-Education-England/feat/xrayTracing
Browse files Browse the repository at this point in the history
feat: AWS xray tracing config
  • Loading branch information
Judge40 authored Sep 4, 2023
2 parents a378da9 + 4b25b9f commit 4005402
Show file tree
Hide file tree
Showing 17 changed files with 309 additions and 4 deletions.
6 changes: 5 additions & 1 deletion .aws/task-definition-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -116,7 +120,7 @@
"value": "eu-west-2"
},
{
"name": "SENTRY_ENVIRONMENT",
"name": "ENVIRONMENT",
"value": "${environment}"
}
]
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# TIS Trainee Credentials

## About

This service issues and verifies trainee digital credentials.

## Developing
Expand All @@ -17,19 +18,20 @@ 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. | |
| GATEWAY_TOKEN_SIGNING_KEY | The Base64 encoded signing key for the credential data. | |
| 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. `<host>/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
Expand Down
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {
}

group = "uk.nhs.tis.trainee"
version = "0.16.0"
version = "0.17.0"

configurations {
compileOnly {
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -50,6 +51,7 @@
@Slf4j
@RestController()
@RequestMapping("/api/issue")
@XRayEnabled
public class IssueResource {

private final IssuanceService service;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -46,6 +47,7 @@
@RestController
@RequestMapping("/api/verify")
@Validated
@XRayEnabled
public class VerifyResource {

private final VerificationService service;
Expand Down
Original file line number Diff line number Diff line change
@@ -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> ecsMetadata, ObjectMapper mapper) {
this.ecsMetadata = ecsMetadata.orElse(null);
this.mapper = mapper;
}

@Override
protected Map<String, Map<String, Object>> generateMetadata(ProceedingJoinPoint pjp,
Subsegment subsegment) {
Map<String, Map<String, Object>> metadata = super.generateMetadata(pjp, subsegment);

Map<String, Object> 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() {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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:}')")
Expand All @@ -53,13 +56,27 @@ 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,

@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,
Expand All @@ -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")
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,45 @@

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.
*/
@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<AWSXRayServletFilter> registerTracingFilter(
@Value("${application.environment}") String environment) {
FilterRegistrationBean<AWSXRayServletFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new AWSXRayServletFilter("tis-trainee-credentials-" + environment));
return registrationBean;
}

/**
* Register a {@link SignedDataFilter}.
*
* @param filter The filter to register.
* @return The {@link FilterRegistrationBean} for the registration.
*/
@Bean
@Order(1)
public FilterRegistrationBean<SignedDataFilter> registerSignedDataFilter(
SignedDataFilter filter) {
FilterRegistrationBean<SignedDataFilter> registrationBean = new FilterRegistrationBean<>();
Expand All @@ -55,6 +77,7 @@ public FilterRegistrationBean<SignedDataFilter> registerSignedDataFilter(
* @return The {@link FilterRegistrationBean} for the registration.
*/
@Bean
@Order(1)
public FilterRegistrationBean<VerifiedSessionFilter> registerVerifiedSessionFilter(
VerifiedSessionFilter filter) {
FilterRegistrationBean<VerifiedSessionFilter> registrationBean = new FilterRegistrationBean<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -50,6 +51,7 @@
*/
@Slf4j
@Service
@XRayEnabled
public class GatewayService {

private static final String CLIENT_ID = "client_id";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -45,6 +46,7 @@
*/
@Slf4j
@Service
@XRayEnabled
public class IssuanceService {

private final GatewayService gatewayService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -41,6 +42,7 @@
*/
@Slf4j
@Service
@XRayEnabled
public class JwtService {

private final ObjectMapper mapper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -39,6 +40,7 @@
*/
@Slf4j
@Service
@XRayEnabled
public class RevocationService {

private final CredentialMetadataRepository credentialMetadataRepository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -45,6 +46,7 @@
*/
@Slf4j
@Service
@XRayEnabled
public class VerificationService {

private static final String CREDENTIAL_PREFIX = "openid ";
Expand Down
9 changes: 8 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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:
Expand Down
Loading

0 comments on commit 4005402

Please sign in to comment.