diff --git a/pom.xml b/pom.xml index 247d0a7..c8042b0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.uid2 uid2-core - 2.20.0 + 2.20.1-alpha-50-SNAPSHOT UTF-8 @@ -24,7 +24,7 @@ com.uid2.core.vertx.CoreVerticle io.vertx.core.Launcher - 7.19.0 + 7.20.0 ${project.version} diff --git a/src/main/java/com/uid2/core/handler/AttestationFailureHandler.java b/src/main/java/com/uid2/core/handler/AttestationFailureHandler.java index 6d78c20..390f756 100644 --- a/src/main/java/com/uid2/core/handler/AttestationFailureHandler.java +++ b/src/main/java/com/uid2/core/handler/AttestationFailureHandler.java @@ -1,10 +1,10 @@ package com.uid2.core.handler; import com.uid2.core.Const; -import com.uid2.core.vertx.AttestationFailureReason; import com.uid2.shared.auth.IAuthorizable; import com.uid2.shared.auth.OperatorKey; import com.uid2.shared.middleware.AuthMiddleware; +import com.uid2.shared.secure.AttestationFailure; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.core.net.SocketAddress; @@ -40,14 +40,14 @@ private void logAttestationFailure(RoutingContext context) { return; } - final AttestationFailureReason attestationFailureReason = context.get(Const.RoutingContextData.ATTESTATION_FAILURE_REASON_PROP); + final AttestationFailure attestationFailure = context.get(Const.RoutingContextData.ATTESTATION_FAILURE_REASON_PROP); final String attestationFailureDataJson = getAttestationFailureDataJson(context); final String originatingIpAddress = getOriginatingIpAddress(context); LOG.warn("Attestation failed. StatusCode={} Reason={} Data={} OperatorKeyHash={} OperatorKeyName={} SiteId={} Protocol={} OperatorType={} OriginatingIpAddress={}", context.response().getStatusCode(), - attestationFailureReason, + attestationFailure, attestationFailureDataJson, operatorKey.getKeyHash(), operatorKey.getName(), diff --git a/src/main/java/com/uid2/core/vertx/AttestationFailureReason.java b/src/main/java/com/uid2/core/vertx/AttestationFailureReason.java deleted file mode 100644 index a262098..0000000 --- a/src/main/java/com/uid2/core/vertx/AttestationFailureReason.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.uid2.core.vertx; - -public enum AttestationFailureReason { - /** - * Request body is not valid JSON. - */ - REQUEST_BODY_IS_NOT_VALID_JSON, - /** - * No attestation request in the request body. - */ - NO_ATTESTATION_REQUEST_ATTACHED, - /** - * Exception occurred encrypting the response. - */ - RESPONSE_ENCRYPTION_EXCEPTION, - /** - * Invalid protocol specified. - */ - INVALID_PROTOCOL, - /** - * Attestation was attempted, but failed. - */ - ATTESTATION_FAILURE, - - /** - * Internal server error - */ - INTERNAL_ERROR, -} diff --git a/src/main/java/com/uid2/core/vertx/CoreVerticle.java b/src/main/java/com/uid2/core/vertx/CoreVerticle.java index 1f4d2ba..00eccda 100644 --- a/src/main/java/com/uid2/core/vertx/CoreVerticle.java +++ b/src/main/java/com/uid2/core/vertx/CoreVerticle.java @@ -49,6 +49,7 @@ import java.security.spec.X509EncodedKeySpec; import java.time.Instant; import java.util.*; + import com.uid2.shared.store.reader.RotatingS3KeyProvider; import com.uid2.shared.model.S3Key; @@ -78,7 +79,6 @@ public class CoreVerticle extends AbstractVerticle { private final ISaltMetadataProvider saltMetadataProvider; private final IPartnerMetadataProvider partnerMetadataProvider; private final OperatorJWTTokenProvider operatorJWTTokenProvider; - private final JwtService jwtService; private final RotatingS3KeyProvider s3KeyProvider; public CoreVerticle(ICloudStorage cloudStorage, @@ -96,7 +96,6 @@ public CoreVerticle(ICloudStorage cloudStorage, this.attestationService = attestationService; this.attestationTokenService = attestationTokenService; - this.jwtService = jwtService; this.enclaveIdentifierProvider = enclaveIdentifierProvider; this.enclaveIdentifierProvider.addListener(this.attestationService); this.s3KeyProvider = s3KeyProvider; @@ -108,7 +107,7 @@ public CoreVerticle(ICloudStorage cloudStorage, enforceJwt = false; } - this.attestationMiddleware = new AttestationMiddleware(this.attestationTokenService, this.jwtService, jwtAudience, jwtIssuer, enforceJwt); + this.attestationMiddleware = new AttestationMiddleware(this.attestationTokenService, jwtService, jwtAudience, jwtIssuer, enforceJwt); this.auth = new AuthMiddleware(authProvider); @@ -225,7 +224,7 @@ private void handleAttestAsync(RoutingContext rc) { try { json = rc.body().asJsonObject(); } catch (DecodeException e) { - setAttestationFailureReason(rc, AttestationFailureReason.REQUEST_BODY_IS_NOT_VALID_JSON); + setAttestationFailureReason(rc, AttestationFailure.BAD_PAYLOAD, Collections.singletonMap("cause", AttestationFailure.BAD_PAYLOAD.explain())); Error("request body is not a valid json", 400, rc, null); return; } @@ -233,7 +232,7 @@ private void handleAttestAsync(RoutingContext rc) { String request = json == null ? null : json.getString("attestation_request"); if (request == null || request.isEmpty()) { - setAttestationFailureReason(rc, AttestationFailureReason.NO_ATTESTATION_REQUEST_ATTACHED); + setAttestationFailureReason(rc, AttestationFailure.BAD_PAYLOAD, Collections.singletonMap("cause", AttestationFailure.BAD_PAYLOAD.explain())); Error("no attestation_request attached", 400, rc, null); return; } @@ -243,7 +242,15 @@ private void handleAttestAsync(RoutingContext rc) { try { attestationService.attest(protocol, request, clientPublicKey, ar -> { if (!ar.succeeded()) { - setAttestationFailureReason(rc, AttestationFailureReason.ATTESTATION_FAILURE, Collections.singletonMap("cause", ar.cause().getMessage())); + if (ar.cause() instanceof AttestationClientException ace && ace.IsClientError()) { + setAttestationFailureReason(rc, ace.getAttestationFailure(), Collections.singletonMap("reason", ace.getAttestationFailure().explain())); + logger.warn("attestation failure: ", ace); + Error("attestation failure", 400, rc, ace.getAttestationFailure().explain()); + return; + } + + // 500 is only for unknown errors in the attestation processing + setAttestationFailureReason(rc, AttestationFailure.INTERNAL_ERROR, Collections.singletonMap("cause", ar.cause().getMessage())); logger.warn("attestation failure: ", ar.cause()); Error("attestation failure", 500, rc, null); return; @@ -251,14 +258,28 @@ private void handleAttestAsync(RoutingContext rc) { final AttestationResult attestationResult = ar.result(); if (!attestationResult.isSuccess()) { - setAttestationFailureReason(rc, AttestationFailureReason.ATTESTATION_FAILURE, Collections.singletonMap("reason", attestationResult.getReason())); - Error(attestationResult.getReason(), 401, rc, null); - return; + AttestationFailure failure = attestationResult.getFailure(); + switch (failure) { + case AttestationFailure.BAD_FORMAT: + case AttestationFailure.INVALID_PROTOCOL: + case AttestationFailure.BAD_CERTIFICATE: + case AttestationFailure.BAD_PAYLOAD: + case AttestationFailure.UNKNOWN_ATTESTATION_URL: + case AttestationFailure.FORBIDDEN_ENCLAVE: + setAttestationFailureReason(rc, failure, Collections.singletonMap("reason", attestationResult.getReason())); + Error(attestationResult.getReason(), 403, rc, failure.explain()); + return; + case AttestationFailure.UNKNOWN: + case AttestationFailure.INTERNAL_ERROR: + setAttestationFailureReason(rc, failure, Collections.singletonMap("reason", attestationResult.getReason())); + Error(attestationResult.getReason(), 500, rc, failure.explain()); + return; + } } if (json.containsKey("operator_type") && !operator.getOperatorType().name().equalsIgnoreCase(json.getString("operator_type"))) { - setAttestationFailureReason(rc, AttestationFailureReason.ATTESTATION_FAILURE); - Error("attestation failure; invalid operator type", 400, rc, null); + setAttestationFailureReason(rc, AttestationFailure.INVALID_TYPE, Collections.singletonMap("reason", AttestationFailure.INVALID_TYPE.explain())); + Error("attestation failure; invalid operator type", 403, rc, null); return; } @@ -294,7 +315,7 @@ private void handleAttestAsync(RoutingContext rc) { } } } catch (Exception e) { - Error("attestation failure", 500, rc, null); + Error("attestation failure", 500, rc, AttestationFailure.INTERNAL_ERROR.explain()); return; } @@ -302,8 +323,8 @@ private void handleAttestAsync(RoutingContext rc) { Success(rc, responseObj); }); } catch (AttestationService.NotFound e) { - setAttestationFailureReason(rc, AttestationFailureReason.INVALID_PROTOCOL); - Error("protocol not found", 500, rc, null); + setAttestationFailureReason(rc, AttestationFailure.INVALID_PROTOCOL, Collections.singletonMap("cause", AttestationFailure.INVALID_PROTOCOL.explain())); + Error("protocol not found", 403, rc, null); } } @@ -316,7 +337,7 @@ private static String encodeAttestationToken(RoutingContext rc, AttestationResul cipher.init(Cipher.ENCRYPT_MODE, publicKey); return Base64.getEncoder().encodeToString(cipher.doFinal(encodedAttestationToken.getBytes(StandardCharsets.UTF_8))); } catch (Exception e) { - setAttestationFailureReason(rc, AttestationFailureReason.RESPONSE_ENCRYPTION_EXCEPTION, Collections.singletonMap("exception", e.getMessage())); + setAttestationFailureReason(rc, AttestationFailure.RESPONSE_ENCRYPTION_ERROR, Collections.singletonMap("exception", e.getMessage())); logger.warn("attestation failure: exception while encrypting response", e); throw e; } @@ -333,10 +354,9 @@ private Map.Entry getJWTTokens(RoutingContext rc, IAuthorizable String optOutJwtToken = this.operatorJWTTokenProvider.getOptOutJWTToken(operator.getKeyHash(), operator.getName(), operator.getRoles(), operator.getSiteId(), enclaveId, operator.getProtocol(), clientVersion, expiresAt); String coreJwtToken = this.operatorJWTTokenProvider.getCoreJWTToken(operator.getKeyHash(), operator.getName(), operator.getRoles(), operator.getSiteId(), enclaveId, operator.getProtocol(), clientVersion, expiresAt); - Map.Entry tokens = new AbstractMap.SimpleEntry<>(optOutJwtToken, coreJwtToken); - return tokens; + return new AbstractMap.SimpleEntry<>(optOutJwtToken, coreJwtToken); } catch (JWTTokenProvider.JwtSigningException e) { - setAttestationFailureReason(rc, AttestationFailureReason.INTERNAL_ERROR, Collections.singletonMap("exception", e.getMessage())); + setAttestationFailureReason(rc, AttestationFailure.INTERNAL_ERROR, Collections.singletonMap("exception", e.getMessage())); logger.error("OptOut JWT token generation failed", e); throw e; } @@ -346,11 +366,11 @@ private Map.Entry getJWTTokens(RoutingContext rc, IAuthorizable return null; } - private static void setAttestationFailureReason(RoutingContext context, AttestationFailureReason reason) { + private static void setAttestationFailureReason(RoutingContext context, AttestationFailure reason) { setAttestationFailureReason(context, reason, null); } - private static void setAttestationFailureReason(RoutingContext context, AttestationFailureReason reason, Map data) { + private static void setAttestationFailureReason(RoutingContext context, AttestationFailure reason, Map data) { context.put(com.uid2.core.Const.RoutingContextData.ATTESTATION_FAILURE_REASON_PROP, reason); context.put(com.uid2.core.Const.RoutingContextData.ATTESTATION_FAILURE_DATA_PROP, data); } @@ -523,14 +543,13 @@ JsonObject make(String name, String failReason) { } Object enclavesObj = main.getValue("enclaves"); - if (!(enclavesObj instanceof JsonArray)) { + if (!(enclavesObj instanceof JsonArray enclaves)) { logger.info("enclave register has been called without .enclaves key"); Error("error", 400, rc, ".enclaves needs to be an array"); return; } JsonArray res = new JsonArray(); - JsonArray enclaves = (JsonArray) enclavesObj; for (int i = 0; i < enclaves.size(); i++) { Result result = new Result(); JsonObject item = enclaves.getJsonObject(i); @@ -573,7 +592,11 @@ private static String getClientVersionFromHeader(RoutingContext rc, IAuthorizabl String clientVersion = "unknown client version"; if (rc.request().headers().contains(Const.Http.AppVersionHeader)) { var client = VertxUtils.parseClientAppVersion(rc.request().headers().get(Const.Http.AppVersionHeader)); - clientVersion = profile.getContact() + "|" + client.getKey() + "|" + client.getValue(); + if (client != null) { + clientVersion = profile.getContact() + "|" + client.getKey() + "|" + client.getValue(); + } else { + clientVersion = profile.getContact() + "|null client key"; + } } return clientVersion; } diff --git a/src/test/java/com/uid2/core/vertx/TestCoreVerticle.java b/src/test/java/com/uid2/core/vertx/TestCoreVerticle.java index 6e7076d..cc1450b 100644 --- a/src/test/java/com/uid2/core/vertx/TestCoreVerticle.java +++ b/src/test/java/com/uid2/core/vertx/TestCoreVerticle.java @@ -32,6 +32,7 @@ import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -102,10 +103,10 @@ private void fakeAuth(Role... roles) { } private void fakeAuth(String attestationProtocol, String operatorType, Role... roles) { - if(operatorType.isEmpty()) { + if (operatorType.isEmpty()) { operatorType = "PRIVATE"; } - OperatorKey operatorKey = new OperatorKey("test-key-hash", "test-key-salt", "test-name", "test-contact", attestationProtocol, 0, false, 88, new HashSet<>(Arrays.asList(roles)), OperatorType.valueOf(operatorType.toUpperCase()), "test-key-id"); + OperatorKey operatorKey = new OperatorKey("test-key-hash", "test-key-salt", "test-name", "test-contact", attestationProtocol, 0, false, 88, new HashSet<>(Arrays.asList(roles)), OperatorType.valueOf(operatorType.toUpperCase()), "test-key-id"); when(authProvider.get(any())).thenReturn(operatorKey); } @@ -156,7 +157,7 @@ private void onHandleAttestationRequest(Callable> f) { private static String makeAttestationRequestJson(String attestationRequest, String publicKey, String operatorType) { JsonObject json = new JsonObject(); - if(!operatorType.isEmpty()) { + if (!operatorType.isEmpty()) { json.put("operator_type", operatorType); } if (attestationRequest != null) { @@ -220,7 +221,7 @@ void attestUnknownAttestationProtocol(Vertx vertx, VertxTestContext testContext) post(vertx, "attest", makeAttestationRequestJson("xxx", "yyy"), ar -> { assertTrue(ar.succeeded()); HttpResponse response = ar.result(); - assertEquals(500, response.statusCode()); + assertEquals(403, response.statusCode()); testContext.completeNow(); }); } @@ -240,18 +241,43 @@ void attestFailureWithAnException(Vertx vertx, VertxTestContext testContext) { }); } - @Test - void attestFailureWithResult(Vertx vertx, VertxTestContext testContext) { + @ParameterizedTest + @EnumSource(value = AttestationFailure.class, names = {"UNKNOWN_ATTESTATION_URL", "FORBIDDEN_ENCLAVE", "BAD_FORMAT", "INVALID_PROTOCOL", "BAD_CERTIFICATE", "BAD_PAYLOAD"}) + void attestFailureWithResultClientError(AttestationFailure failure, Vertx vertx, VertxTestContext testContext) { fakeAuth(Role.OPERATOR); addAttestationProvider(attestationProtocol); onHandleAttestationRequest(() -> { - return Future.succeededFuture(new AttestationResult(AttestationFailure.BAD_PAYLOAD)); + return Future.succeededFuture(new AttestationResult(failure)); }); post(vertx, "attest", makeAttestationRequestJson("xxx", "yyy"), ar -> { - assertTrue(ar.succeeded()); - HttpResponse response = ar.result(); - assertEquals(401, response.statusCode()); - testContext.completeNow(); + try { + assertTrue(ar.succeeded()); + HttpResponse response = ar.result(); + assertEquals(403, response.statusCode()); + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } + }); + } + + @ParameterizedTest + @EnumSource(value = AttestationFailure.class, names = {"UNKNOWN", "INTERNAL_ERROR"}) + void attestFailureWithResultServerError(AttestationFailure failure, Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.OPERATOR); + addAttestationProvider(attestationProtocol); + onHandleAttestationRequest(() -> { + return Future.succeededFuture(new AttestationResult(failure)); + }); + post(vertx, "attest", makeAttestationRequestJson("xxx", "yyy"), ar -> { + try { + assertTrue(ar.succeeded()); + HttpResponse response = ar.result(); + assertEquals(500, response.statusCode()); + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @@ -267,15 +293,19 @@ void attestSuccessNoEncryption(String operatorType, Vertx vertx, VertxTestContex EncryptedAttestationToken encryptedAttestationToken = new EncryptedAttestationToken("test-attestation-token", Instant.ofEpochMilli(111)); when(attestationTokenService.createToken(any())).thenReturn(encryptedAttestationToken); post(vertx, "attest", makeAttestationRequestJson("xxx", "yyy"), ar -> { - assertTrue(ar.succeeded()); - HttpResponse response = ar.result(); - assertEquals(200, response.statusCode()); - JsonObject json = response.bodyAsJsonObject(); - String attestationToken = json.getJsonObject("body").getString("attestation_token"); - String expiresAt = json.getJsonObject("body").getString("expiresAt"); - assertEquals("test-attestation-token", attestationToken); - assertEquals("1970-01-01T00:00:00.111Z", expiresAt); - testContext.completeNow(); + try { + assertTrue(ar.succeeded()); + HttpResponse response = ar.result(); + assertEquals(200, response.statusCode()); + JsonObject json = response.bodyAsJsonObject(); + String attestationToken = json.getJsonObject("body").getString("attestation_token"); + String expiresAt = json.getJsonObject("body").getString("expiresAt"); + assertEquals("test-attestation-token", attestationToken); + assertEquals("1970-01-01T00:00:00.111Z", expiresAt); + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @@ -291,12 +321,16 @@ void attestOperatorTypeMismatchNoEncryption(String operatorType, Vertx vertx, Ve EncryptedAttestationToken encryptedAttestationToken = new EncryptedAttestationToken("test-attestation-token", Instant.ofEpochMilli(111)); when(attestationTokenService.createToken(any())).thenReturn(encryptedAttestationToken); post(vertx, "attest", makeAttestationRequestJson("xxx", "yyy", operatorType.equalsIgnoreCase("public") ? "private" : "public"), ar -> { - assertTrue(ar.succeeded()); - HttpResponse response = ar.result(); - assertEquals(400, response.statusCode()); - JsonObject json = response.bodyAsJsonObject(); - assertEquals("attestation failure; invalid operator type", json.getString("status")); - testContext.completeNow(); + try { + assertTrue(ar.succeeded()); + HttpResponse response = ar.result(); + assertEquals(403, response.statusCode()); + JsonObject json = response.bodyAsJsonObject(); + assertEquals("attestation failure; invalid operator type", json.getString("status")); + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @@ -316,24 +350,28 @@ void attestSuccessWithEncryption(String operatorType, Vertx vertx, VertxTestCont EncryptedAttestationToken encryptedAttestationToken = new EncryptedAttestationToken("test-attestation-token", Instant.ofEpochMilli(111)); when(attestationTokenService.createToken(any())).thenReturn(encryptedAttestationToken); post(vertx, "attest", makeAttestationRequestJson("xxx", "yyy", operatorType), ar -> { - assertTrue(ar.succeeded()); - HttpResponse response = ar.result(); - assertEquals(200, response.statusCode()); - JsonObject json = response.bodyAsJsonObject(); - String attestationToken = json.getJsonObject("body").getString("attestation_token"); - assertNotEquals("", attestationToken); - String expiresAt = json.getJsonObject("body").getString("expiresAt"); - assertEquals("1970-01-01T00:00:00.111Z", expiresAt); - String[] decryptedAttestationToken = {""}; - assertDoesNotThrow(() -> { - Cipher cipher = Cipher.getInstance(Const.Name.AsymetricEncryptionCipherClass); - cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); - decryptedAttestationToken[0] = new String(cipher.doFinal(Base64.getDecoder().decode(attestationToken))); - }); - - assertEquals("test-attestation-token", decryptedAttestationToken[0]); + try { + assertTrue(ar.succeeded()); + HttpResponse response = ar.result(); + assertEquals(200, response.statusCode()); + JsonObject json = response.bodyAsJsonObject(); + String attestationToken = json.getJsonObject("body").getString("attestation_token"); + assertNotEquals("", attestationToken); + String expiresAt = json.getJsonObject("body").getString("expiresAt"); + assertEquals("1970-01-01T00:00:00.111Z", expiresAt); + String[] decryptedAttestationToken = {""}; + assertDoesNotThrow(() -> { + Cipher cipher = Cipher.getInstance(Const.Name.AsymetricEncryptionCipherClass); + cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); + decryptedAttestationToken[0] = new String(cipher.doFinal(Base64.getDecoder().decode(attestationToken))); + }); - testContext.completeNow(); + assertEquals("test-attestation-token", decryptedAttestationToken[0]); + + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @@ -354,12 +392,16 @@ void attestOperatorTypeMismatchWithEncryption(String operatorType, Vertx vertx, EncryptedAttestationToken encryptedAttestationToken = new EncryptedAttestationToken("test-attestation-token", Instant.ofEpochMilli(111)); when(attestationTokenService.createToken(any())).thenReturn(encryptedAttestationToken); post(vertx, "attest", makeAttestationRequestJson("xxx", "yyy", operatorType.equalsIgnoreCase("public") ? "private" : "public"), ar -> { - assertTrue(ar.succeeded()); - HttpResponse response = ar.result(); - assertEquals(400, response.statusCode()); - JsonObject json = response.bodyAsJsonObject(); - assertEquals("attestation failure; invalid operator type", json.getString("status")); - testContext.completeNow(); + try { + assertTrue(ar.succeeded()); + HttpResponse response = ar.result(); + assertEquals(403, response.statusCode()); + JsonObject json = response.bodyAsJsonObject(); + assertEquals("attestation failure; invalid operator type", json.getString("status")); + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @@ -378,25 +420,30 @@ void attestSuccessWithEncryptionNoPublicKeyOnRequest(Vertx vertx, VertxTestConte EncryptedAttestationToken encryptedAttestationToken = new EncryptedAttestationToken("test-attestation-token", Instant.ofEpochMilli(111)); when(attestationTokenService.createToken(any())).thenReturn(encryptedAttestationToken); post(vertx, "attest", makeAttestationRequestJson("xxx", null), ar -> { - assertTrue(ar.succeeded()); - HttpResponse response = ar.result(); - assertEquals(200, response.statusCode()); - JsonObject json = response.bodyAsJsonObject(); - String attestationToken = json.getJsonObject("body").getString("attestation_token"); - assertNotEquals("", attestationToken); - String expiresAt = json.getJsonObject("body").getString("expiresAt"); - assertEquals("1970-01-01T00:00:00.111Z", expiresAt); - - String[] decryptedAttestationToken = {""}; - assertDoesNotThrow(() -> { - Cipher cipher = Cipher.getInstance(Const.Name.AsymetricEncryptionCipherClass); - cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); - decryptedAttestationToken[0] = new String(cipher.doFinal(Base64.getDecoder().decode(attestationToken))); - }); - - assertEquals("test-attestation-token", decryptedAttestationToken[0]); + try { + assertTrue(ar.succeeded()); - testContext.completeNow(); + HttpResponse response = ar.result(); + assertEquals(200, response.statusCode()); + JsonObject json = response.bodyAsJsonObject(); + String attestationToken = json.getJsonObject("body").getString("attestation_token"); + assertNotEquals("", attestationToken); + String expiresAt = json.getJsonObject("body").getString("expiresAt"); + assertEquals("1970-01-01T00:00:00.111Z", expiresAt); + + String[] decryptedAttestationToken = {""}; + assertDoesNotThrow(() -> { + Cipher cipher = Cipher.getInstance(Const.Name.AsymetricEncryptionCipherClass); + cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); + decryptedAttestationToken[0] = new String(cipher.doFinal(Base64.getDecoder().decode(attestationToken))); + }); + + assertEquals("test-attestation-token", decryptedAttestationToken[0]); + + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @@ -417,22 +464,26 @@ void attestOptOutJWTCalledUnknownClient(Vertx vertx, VertxTestContext testContex when(operatorJWTTokenProvider.getOptOutJWTToken("test-key-hash", "test-name", Set.of(Role.OPERATOR, Role.OPTOUT), 88, "test-enclaveId", attestationProtocol, "unknown client version", Instant.ofEpochMilli(111))).thenReturn("dummy_token_optout"); when(operatorJWTTokenProvider.getCoreJWTToken("test-key-hash", "test-name", Set.of(Role.OPERATOR, Role.OPTOUT), 88, "test-enclaveId", attestationProtocol, "unknown client version", Instant.ofEpochMilli(111))).thenReturn("dummy_token_core"); post(vertx, "attest", makeAttestationRequestJson("xxx", null), ar -> { - assertTrue(ar.succeeded()); - HttpResponse response = ar.result(); - try { - verify(operatorJWTTokenProvider, times(1)).getCoreJWTToken("test-key-hash", "test-name", Set.of(Role.OPERATOR, Role.OPTOUT), 88, "test-enclaveId", attestationProtocol, "unknown client version", Instant.ofEpochMilli(111)); - verify(operatorJWTTokenProvider, times(1)).getOptOutJWTToken("test-key-hash", "test-name", Set.of(Role.OPERATOR, Role.OPTOUT), 88, "test-enclaveId", attestationProtocol, "unknown client version", Instant.ofEpochMilli(111)); - JsonObject json = response.bodyAsJsonObject(); - String jwtOptout = json.getJsonObject("body").getString("attestation_jwt_optout"); - String jwtCore = json.getJsonObject("body").getString("attestation_jwt_core"); - assertEquals("dummy_token_optout", jwtOptout); - assertEquals("dummy_token_core", jwtCore); - } catch (Exception e) { - testContext.failNow(e); - } + assertTrue(ar.succeeded()); + HttpResponse response = ar.result(); + + try { + verify(operatorJWTTokenProvider, times(1)).getCoreJWTToken("test-key-hash", "test-name", Set.of(Role.OPERATOR, Role.OPTOUT), 88, "test-enclaveId", attestationProtocol, "unknown client version", Instant.ofEpochMilli(111)); + verify(operatorJWTTokenProvider, times(1)).getOptOutJWTToken("test-key-hash", "test-name", Set.of(Role.OPERATOR, Role.OPTOUT), 88, "test-enclaveId", attestationProtocol, "unknown client version", Instant.ofEpochMilli(111)); + JsonObject json = response.bodyAsJsonObject(); + String jwtOptout = json.getJsonObject("body").getString("attestation_jwt_optout"); + String jwtCore = json.getJsonObject("body").getString("attestation_jwt_core"); + assertEquals("dummy_token_optout", jwtOptout); + assertEquals("dummy_token_core", jwtCore); + } catch (Exception e) { + testContext.failNow(e); + } - testContext.completeNow(); + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @@ -509,10 +560,14 @@ void multipartRequestWrongMethodForMultipart(Vertx vertx, VertxTestContext testC form.set("secondValue", "value2"); get(vertx, "/sites/refresh", form, (ar) -> { - HttpResponse response = ar.result(); - assertEquals(400, response.statusCode()); - assertEquals("Content-Type \"multipart/*\" Not Allowed\"", response.bodyAsString()); - testContext.completeNow(); + try { + HttpResponse response = ar.result(); + assertEquals(400, response.statusCode()); + assertEquals("Content-Type \"multipart/*\" Not Allowed\"", response.bodyAsString()); + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @@ -523,30 +578,42 @@ void multipartRequestWrongMethodForEndpoint(Vertx vertx, VertxTestContext testCo form.set("secondValue", "value2"); post(vertx, "/sites/refresh", form, (ar) -> { - HttpResponse response = ar.result(); - assertEquals(405, response.statusCode()); - assertEquals("Method Not Allowed", response.statusMessage()); - testContext.completeNow(); + try { + HttpResponse response = ar.result(); + assertEquals(405, response.statusCode()); + assertEquals("Method Not Allowed", response.statusMessage()); + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @Test void wrongMethodForEndpoint(Vertx vertx, VertxTestContext testContext) { post(vertx, "/sites/refresh", makeAttestationRequestJson(null, null), ar -> { - HttpResponse response = ar.result(); - assertEquals(405, response.statusCode()); - assertEquals("Method Not Allowed", response.statusMessage()); - testContext.completeNow(); + try { + HttpResponse response = ar.result(); + assertEquals(405, response.statusCode()); + assertEquals("Method Not Allowed", response.statusMessage()); + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @Test void wrongMethodForEndpointS3(Vertx vertx, VertxTestContext testContext) { post(vertx, "/s3encryption_keys/retrieve", makeAttestationRequestJson(null, null), ar -> { - HttpResponse response = ar.result(); - assertEquals(405, response.statusCode()); - assertEquals("Method Not Allowed", response.statusMessage()); - testContext.completeNow(); + try { + HttpResponse response = ar.result(); + assertEquals(405, response.statusCode()); + assertEquals("Method Not Allowed", response.statusMessage()); + testContext.completeNow(); + } catch (Throwable ex) { + testContext.failNow(ex); + } }); } @@ -566,26 +633,30 @@ void s3encryptionKeyRetrieveSuccess(Vertx vertx, VertxTestContext testContext) { when(s3KeyProvider.getKeys(88)).thenReturn(keys); get(vertx, "s3encryption_keys/retrieve", ar -> { - if (ar.succeeded()) { - HttpResponse response = ar.result(); - assertEquals(200, response.statusCode()); + try { + if (ar.succeeded()) { + HttpResponse response = ar.result(); + assertEquals(200, response.statusCode()); - JsonObject json = response.bodyAsJsonObject(); - JsonArray s3KeysArray = json.getJsonArray("s3Keys"); + JsonObject json = response.bodyAsJsonObject(); + JsonArray s3KeysArray = json.getJsonArray("s3Keys"); - assertNotNull(s3KeysArray); - assertEquals(1, s3KeysArray.size()); + assertNotNull(s3KeysArray); + assertEquals(1, s3KeysArray.size()); - JsonObject s3KeyJson = s3KeysArray.getJsonObject(0); - assertEquals(1, s3KeyJson.getInteger("id")); - assertEquals(88, s3KeyJson.getInteger("siteId")); - assertEquals(1687635529, s3KeyJson.getLong("activates")); - assertEquals(1687808329, s3KeyJson.getLong("created")); - assertEquals("newSecret", s3KeyJson.getString("secret")); + JsonObject s3KeyJson = s3KeysArray.getJsonObject(0); + assertEquals(1, s3KeyJson.getInteger("id")); + assertEquals(88, s3KeyJson.getInteger("siteId")); + assertEquals(1687635529, s3KeyJson.getLong("activates")); + assertEquals(1687808329, s3KeyJson.getLong("created")); + assertEquals("newSecret", s3KeyJson.getString("secret")); - testContext.completeNow(); - } else { - testContext.failNow(ar.cause()); + testContext.completeNow(); + } else { + testContext.failNow(ar.cause()); + } + } catch (Throwable ex) { + testContext.failNow(ex); } }); } @@ -610,28 +681,32 @@ void s3encryptionKeyRetrieveSuccessWithThreeKeys(Vertx vertx, VertxTestContext t when(s3KeyProvider.getKeys(88)).thenReturn(keys); get(vertx, "s3encryption_keys/retrieve", ar -> { - if (ar.succeeded()) { - HttpResponse response = ar.result(); - assertEquals(200, response.statusCode()); - - JsonObject json = response.bodyAsJsonObject(); - JsonArray s3KeysArray = json.getJsonArray("s3Keys"); - - assertNotNull(s3KeysArray); - assertEquals(3, s3KeysArray.size()); + try { + if (ar.succeeded()) { + HttpResponse response = ar.result(); + assertEquals(200, response.statusCode()); + + JsonObject json = response.bodyAsJsonObject(); + JsonArray s3KeysArray = json.getJsonArray("s3Keys"); + + assertNotNull(s3KeysArray); + assertEquals(3, s3KeysArray.size()); + + for (int i = 0; i < 3; i++) { + JsonObject s3KeyJson = s3KeysArray.getJsonObject(i); + assertEquals(i + 1, s3KeyJson.getInteger("id")); + assertEquals(88, s3KeyJson.getInteger("siteId")); + assertEquals(1687635529 + i, s3KeyJson.getLong("activates")); + assertEquals(1687808329 + i, s3KeyJson.getLong("created")); + assertEquals("secret" + (i + 1), s3KeyJson.getString("secret")); + } - for (int i = 0; i < 3; i++) { - JsonObject s3KeyJson = s3KeysArray.getJsonObject(i); - assertEquals(i + 1, s3KeyJson.getInteger("id")); - assertEquals(88, s3KeyJson.getInteger("siteId")); - assertEquals(1687635529 + i, s3KeyJson.getLong("activates")); - assertEquals(1687808329 + i, s3KeyJson.getLong("created")); - assertEquals("secret" + (i + 1), s3KeyJson.getString("secret")); + testContext.completeNow(); + } else { + testContext.failNow(ar.cause()); } - - testContext.completeNow(); - } else { - testContext.failNow(ar.cause()); + } catch (Throwable ex) { + testContext.failNow(ex); } }); } @@ -650,34 +725,38 @@ void s3encryptionKeyRetrieveNoKeysOrError(Vertx vertx, VertxTestContext testCont when(s3KeyProvider.getKeys(anyInt())).thenReturn(Collections.emptyList()); get(vertx, "s3encryption_keys/retrieve", ar -> { - if (ar.succeeded()) { - HttpResponse response = ar.result(); - assertEquals(500, response.statusCode()); - - JsonObject json = response.bodyAsJsonObject(); - assertEquals("No S3 keys found", json.getString("status")); - assertTrue(json.getString("message").contains("No S3 keys found for siteId:")); - - // Test case 2: Exception thrown - when(s3KeyProvider.getKeys(anyInt())).thenThrow(new RuntimeException("Test exception")); - - get(vertx, "s3encryption_keys/retrieve", ar2 -> { - if (ar2.succeeded()) { - HttpResponse response2 = ar2.result(); - assertEquals(500, response2.statusCode()); - - JsonObject json2 = response2.bodyAsJsonObject(); - System.out.println(json2); - assertEquals("error", json2.getString("status")); - assertEquals("error generating attestation token", json2.getString("message")); - - testContext.completeNow(); - } else { - testContext.failNow(ar2.cause()); - } - }); - } else { - testContext.failNow(ar.cause()); + try { + if (ar.succeeded()) { + HttpResponse response = ar.result(); + assertEquals(500, response.statusCode()); + + JsonObject json = response.bodyAsJsonObject(); + assertEquals("No S3 keys found", json.getString("status")); + assertTrue(json.getString("message").contains("No S3 keys found for siteId:")); + + // Test case 2: Exception thrown + when(s3KeyProvider.getKeys(anyInt())).thenThrow(new RuntimeException("Test exception")); + + get(vertx, "s3encryption_keys/retrieve", ar2 -> { + if (ar2.succeeded()) { + HttpResponse response2 = ar2.result(); + assertEquals(500, response2.statusCode()); + + JsonObject json2 = response2.bodyAsJsonObject(); + System.out.println(json2); + assertEquals("error", json2.getString("status")); + assertEquals("error generating attestation token", json2.getString("message")); + + testContext.completeNow(); + } else { + testContext.failNow(ar2.cause()); + } + }); + } else { + testContext.failNow(ar.cause()); + } + } catch (Throwable ex) { + testContext.failNow(ex); } }); }