Skip to content

Commit

Permalink
Merge pull request #161 from IABTechLab/tjm-UID2-4246-only-shutdown-o…
Browse files Browse the repository at this point in the history
…n-401

Return AttestationFailure or RetryableFailures
  • Loading branch information
thomasm-ttd authored Oct 21, 2024
2 parents ccceef0 + 0dbd2cd commit 455735f
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 218 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.uid2</groupId>
<artifactId>uid2-core</artifactId>
<version>2.20.0</version>
<version>2.20.1-alpha-50-SNAPSHOT</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand All @@ -24,7 +24,7 @@
<vertx.verticle>com.uid2.core.vertx.CoreVerticle</vertx.verticle>
<launcher.class>io.vertx.core.Launcher</launcher.class>

<uid2-shared.version>7.19.0</uid2-shared.version>
<uid2-shared.version>7.20.0</uid2-shared.version>
<image.version>${project.version}</image.version>
</properties>

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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(),
Expand Down
29 changes: 0 additions & 29 deletions src/main/java/com/uid2/core/vertx/AttestationFailureReason.java

This file was deleted.

69 changes: 46 additions & 23 deletions src/main/java/com/uid2/core/vertx/CoreVerticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -225,15 +224,15 @@ 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;
}

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;
}
Expand All @@ -243,22 +242,44 @@ 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;
}

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;
}

Expand Down Expand Up @@ -294,16 +315,16 @@ private void handleAttestAsync(RoutingContext rc) {
}
}
} catch (Exception e) {
Error("attestation failure", 500, rc, null);
Error("attestation failure", 500, rc, AttestationFailure.INTERNAL_ERROR.explain());
return;
}

logger.info("attestation successful for SiteId: {}, Operator name: {}, protocol: {}", operator.getSiteId(), operator.getName(), protocol);
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);
}
}

Expand All @@ -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;
}
Expand All @@ -333,10 +354,9 @@ private Map.Entry<String, String> 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<String, String> 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;
}
Expand All @@ -346,11 +366,11 @@ private Map.Entry<String, String> 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<String, Object> data) {
private static void setAttestationFailureReason(RoutingContext context, AttestationFailure reason, Map<String, Object> 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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit 455735f

Please sign in to comment.