Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AUT-3916: Adding integration tests for MFA Reset #5693

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
ead1354
AUT-3691: Use finalizedBy to complete test cleanly
andrew-moores Dec 20, 2024
7a25fe3
AUT-3691: Remove superfluous configuration of jacoco from the integra…
andrew-moores Dec 20, 2024
f3efdf6
AUT-3691: Remove test filter from delivery-receipts-integration-tests…
andrew-moores Dec 20, 2024
d6fe948
AUT-3691: Added a testLogging configuration to the test task in di-au…
andrew-moores Dec 20, 2024
6109f84
AUT-3691: Removed filter from test task to make it consistent with th…
andrew-moores Dec 20, 2024
c316ddc
AUT-3691: Add test to check the JWKS endpoint serves the public key u…
andrew-moores Dec 30, 2024
3155769
AUT-3691: Add test to check the Storage Token JWKS endpoint serves th…
andrew-moores Dec 30, 2024
4c3af37
AUT-3691: Add tests to check the JWKS lambdas don't allow exceptions …
andrew-moores Dec 30, 2024
0d3978d
AUT-3691: Use the Junit extension for creating KMS keys with the modi…
andrew-moores Dec 31, 2024
a39fb86
AUT-3691: Improve logging to make it clear when keys have been succes…
andrew-moores Dec 31, 2024
8a5f704
AUT-3691: Add integration test for MFAResetAuthorizeHandler.
andrew-moores Jan 2, 2025
9504232
AUT-3691: Add integration test for ReverificationResultHandler and up…
andrew-moores Jan 6, 2025
871aa78
AUT-3691: Update MfaResetAuthorizeHandlerIntegrationTest to create th…
andrew-moores Jan 6, 2025
0d90ca0
AUT-3691: Update MfaResetAuthorizeHandlerIntegrationTest to fix the i…
andrew-moores Jan 6, 2025
6fb67be
AUT-3916: Corrections to tests following rebase.
andrew-moores Jan 17, 2025
dd04adf
AUT-3916: Clarification to key id provided in JWKS
andrew-moores Jan 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pre-merge-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ jobs:
localstack:
image: ${{ (needs.check-changed-files.outputs.java_changed == 'true') && 'localstack/localstack:3.0.0' || '' }}
env:
SERVICES: "lambda, apigateway, iam, ec2, sqs, s3, sts, kms, sns, ssm, events"
SERVICES: "lambda, apigateway, iam, ec2, sqs, s3, sts, kms, sns, ssm, events, logs"
GATEWAY_LISTEN: 0.0.0.0:45678
LOCALSTACK_HOST: localhost:45678
TEST_AWS_ACCOUNT_ID: 123456789012
Expand Down
4 changes: 1 addition & 3 deletions account-management-integration-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ dependencies {

test {
useJUnitPlatform()
filter {
includeTestsMatching "*"
}

environment "AUDIT_SIGNING_KEY_ALIAS", "alias/local-audit-payload-signing-key-alias"
environment "AWS_ACCESS_KEY_ID", "mock-access-key"
environment "AWS_REGION", "eu-west-2"
Expand Down
8 changes: 3 additions & 5 deletions delivery-receipts-integration-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ dependencies {

test {
useJUnitPlatform()
filter {
includeTestsMatching "*"
}

environment "AWS_ACCESS_KEY_ID", "mock-access-key"
environment "AWS_REGION", "eu-west-2"
Expand Down Expand Up @@ -56,14 +53,15 @@ test {

doLast {
tasks.getByName("jacocoTestReport").sourceDirectories.from(
project(":account-management-api").sourceSets.main.java,
project(":delivery-receipts-api").sourceSets.main.java,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the commit message here can we add the detail of what the issue was here and why we think this fixes? Just to have the context there for future (you explained it to me and I've already forgotten :-D)

project(":shared").sourceSets.main.java)

tasks.getByName("jacocoTestReport").classDirectories.from(
project(":account-management-api").sourceSets.main.output,
project(":delivery-receipts-api").sourceSets.main.output,
project(":shared").sourceSets.main.output)
}
dependsOn ":composeUp"
finalizedBy ":composeDown"
}

jacocoTestReport {
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:
image: localstack/localstack:3.1.0@sha256:a47b435f876a100115d1d9f24e19b6b302cc7acb78b91e9258122116283ba462
restart: no
environment:
SERVICES: iam, ec2, sqs, s3, sts, kms, sns, ssm, cloudwatch, events
SERVICES: iam, ec2, sqs, s3, sts, kms, sns, ssm, cloudwatch, events, logs
GATEWAY_LISTEN: 0.0.0.0:45678
LOCALSTACK_HOST: localhost:45678
TEST_AWS_ACCOUNT_ID: 123456789012
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ public APIGatewayProxyResponseEvent handleRequest(
}

public APIGatewayProxyResponseEvent mfaResetJarJwkHandler() {
LOG.info("MFA reset JAR Signing JWK request received");
LOG.info(
"Request for Auth reverification request JAR signature verification key received.");
try {

List<JWK> signingKeys = new ArrayList<>();
Expand All @@ -55,16 +56,20 @@ public APIGatewayProxyResponseEvent mfaResetJarJwkHandler() {

JWKSet jwkSet = new JWKSet(signingKeys);

LOG.info("Generating MFA reset JAR signing JWK successful response");
LOG.info("Served Auth reverification request JAR signature verification key JWK set.");

return generateApiGatewayProxyResponse(
200,
segmentedFunctionCall("serialiseJWKSet", () -> jwkSet.toString(true)),
Map.of("Cache-Control", "max-age=86400"),
null);
} catch (Exception e) {
LOG.error("Error in MfaResetJarJwkHandler lambda", e);
return generateApiGatewayProxyResponse(500, "Error providing MFA Reset JAR JWK data");
LOG.error(
"Failed to serve Auth reverification request JAR signature verification key.",
e);
return generateApiGatewayProxyResponse(
500,
"Auth MFA reverification request JAR signature verification key not available.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,25 @@ public APIGatewayProxyResponseEvent handleRequest(

public APIGatewayProxyResponseEvent mfaResetStorageTokenJwkHandler() {
try {
LOG.info("MfaResetStorageTokenJwk request received");
LOG.info("Request for Auth MFA storage token signature verification key received.");

List<JWK> signingKeys = new ArrayList<>();

signingKeys.add(jwksService.getPublicMfaResetStorageTokenJwkWithOpaqueId());

JWKSet jwkSet = new JWKSet(signingKeys);

LOG.info("Generating MfaResetStorageTokenJwk successful response");
LOG.info("Served Auth MFA storage token signature verification key JWK set.");

return generateApiGatewayProxyResponse(
200,
segmentedFunctionCall("serialiseJWKSet", () -> jwkSet.toString(true)),
Map.of("Cache-Control", "max-age=86400"),
null);
} catch (Exception e) {
LOG.error("Error in MfaResetStorageTokenJwk lambda", e);
LOG.error("Failed to serve Auth MFA storage token signature verification key.", e);
return generateApiGatewayProxyResponse(
500, "Error providing MfaResetStorageTokenJwk data");
500, "Auth MFA storage token signature verification key not available.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class IPVReverificationService {
private static final Logger LOG = LogManager.getLogger(IPVReverificationService.class);
private static final JWSAlgorithm SIGNING_ALGORITHM = JWSAlgorithm.ES256;
private static final String MFA_RESET_SCOPE = "reverification";
private static final String STATE_STORAGE_PREFIX = "mfaReset:state:";
public static final String STATE_STORAGE_PREFIX = "mfaReset:state:";
private final ConfigurationService configurationService;
private final JwtService jwtService;
private final NowClock nowClock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ void shouldReturn500WhenSigningKeyIsNotPresent() {
var result = handler.handleRequest(event, context);

assertThat(result, hasStatus(500));
assertThat(result, hasBody("Error providing MFA Reset JAR JWK data"));
assertThat(
result,
hasBody(
"Auth MFA reverification request JAR signature verification key not available."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ void shouldReturn500WhenSigningKeyIsNotPresent() {
var result = handler.handleRequest(event, context);

assertThat(result, hasStatus(500));
assertThat(result, hasBody("Error providing MfaResetStorageTokenJwk data"));
assertThat(
result,
hasBody("Auth MFA storage token signature verification key not available."));
}

@Test
Expand Down
10 changes: 8 additions & 2 deletions integration-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,16 @@ dependencies {
implementation project(":utils"), noXray

testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${dependencyVersions.junit}"
testImplementation("uk.org.webcompere:system-stubs-jupiter:2.1.3")
testImplementation("com.google.guava:guava:33.3.1-jre")
testImplementation("org.awaitility:awaitility:4.2.0")
testImplementation('org.wiremock:wiremock-jetty12:3.10.0')
}

test {
useJUnitPlatform()
exclude 'uk/gov/di/authentication/contract/**'

jacoco {
}
environment "AUDIT_SIGNING_KEY_ALIAS", "alias/local-audit-payload-signing-key-alias"
environment "AWS_ACCESS_KEY_ID", "mock-access-key"
environment "AWS_REGION", "eu-west-2"
Expand All @@ -69,6 +71,10 @@ test {
environment "BULK_USER_EMAIL_INCLUDED_TERMS_AND_CONDITIONS", "1.0,1.1,1.2,1.3,1.4"
environment "SEND_STORAGE_TOKEN_TO_IPV_ENABLED", "true"

testLogging {
showStandardStreams = false
}

doLast {
tasks.getByName("jacocoTestReport").sourceDirectories.from(
project(":frontend-api").sourceSets.main.java,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package uk.gov.di.authentication.api;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.KeyType;
import com.nimbusds.jose.jwk.KeyUse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kms.KmsClient;
import software.amazon.awssdk.services.kms.model.GetPublicKeyRequest;
import software.amazon.awssdk.services.kms.model.GetPublicKeyResponse;
import software.amazon.awssdk.services.kms.model.KeyUsageType;
import uk.gov.di.authentication.frontendapi.lambda.MfaResetJarJwkHandler;
import uk.gov.di.authentication.shared.services.ConfigurationService;
import uk.gov.di.authentication.sharedtest.basetest.ApiGatewayHandlerIntegrationTest;
import uk.gov.di.authentication.sharedtest.extensions.KmsKeyExtension;
import uk.gov.di.authentication.sharedtest.logging.CaptureLoggingExtension;
import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
import uk.org.webcompere.systemstubs.jupiter.SystemStub;
import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;

import java.net.URI;
import java.util.Map;
import java.util.Optional;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static uk.gov.di.authentication.shared.helpers.HashHelper.hashSha256String;
import static uk.gov.di.authentication.sharedtest.logging.LogEventMatcher.withMessageContaining;
import static uk.gov.di.orchestration.sharedtest.matchers.APIGatewayProxyResponseEventMatcher.hasStatus;

@ExtendWith(SystemStubsExtension.class)
class AuthSigningKeyJWKSIntegrationTest extends ApiGatewayHandlerIntegrationTest {
private static final Logger LOG = LogManager.getLogger(AuthSigningKeyJWKSIntegrationTest.class);

@SystemStub private static final EnvironmentVariables environment = new EnvironmentVariables();

@RegisterExtension
private static final KmsKeyExtension mfaResetJarSigningKey =
new KmsKeyExtension("mfa-reset-jar-signing-key", KeyUsageType.SIGN_VERIFY);

@RegisterExtension
private static final CaptureLoggingExtension logging =
new CaptureLoggingExtension(MfaResetJarJwkHandler.class);

private static String expectedHashKeyArn;

@BeforeAll
static void setupEnvironment() {
environment.set(
"IPV_REVERIFICATION_REQUESTS_SIGNING_KEY_ALIAS", mfaResetJarSigningKey.getKeyId());

try (KmsClient kmsClient = getKmsClient()) {
GetPublicKeyRequest getPublicKeyRequest =
GetPublicKeyRequest.builder().keyId(mfaResetJarSigningKey.getKeyId()).build();

GetPublicKeyResponse getPublicKeyResponse = kmsClient.getPublicKey(getPublicKeyRequest);

expectedHashKeyArn = hashSha256String(getPublicKeyResponse.keyId());
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}

private static KmsClient getKmsClient() {
return KmsClient.builder()
.endpointOverride(URI.create("http://localhost:45678"))
.credentialsProvider(
StaticCredentialsProvider.create(
AwsBasicCredentials.create("dummy", "dummy")))
.region(Region.EU_WEST_2)
.build();
}

@Test
void shouldReturnJWKSetContainingTheReverificationSigningKey() {
handler = new MfaResetJarJwkHandler(new ConfigurationService());

var response = makeRequest(Optional.empty(), Map.of(), Map.of());

assertThat(response, hasStatus(200));

JsonObject jwk = JsonParser.parseString(response.getBody()).getAsJsonObject();
JsonArray keys = jwk.get("keys").getAsJsonArray();
assertEquals(1, keys.size(), "JWKS endpoint must return a single key.");

checkPublicSigningKeyResponseMeetsADR0030(keys.get(0).getAsJsonObject());
}

@Test
void shouldNotAllowExceptionsToEscape() {
environment.set("IPV_REVERIFICATION_REQUESTS_SIGNING_KEY_ALIAS", "wrong-key-alias");

handler = new MfaResetJarJwkHandler(new ConfigurationService());

var response = makeRequest(Optional.empty(), Map.of(), Map.of());

assertThat(response, hasStatus(500));
assertThat(
logging.events(),
hasItem(
withMessageContaining(
"Failed to serve Auth reverification request JAR signature verification key.")));
}

private static void checkPublicSigningKeyResponseMeetsADR0030(JsonObject key) {
assertEquals(expectedHashKeyArn, key.get("kid").getAsString());
assertEquals(KeyType.EC.getValue(), key.get("kty").getAsString());
assertEquals(KeyUse.SIGNATURE.getValue(), key.get("use").getAsString());
assertEquals(Curve.P_256.getName(), key.get("crv").getAsString());
assertEquals(JWSAlgorithm.ES256.toString(), key.get("alg").getAsString());
}
}
Loading
Loading