diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2021-03-22 16:46:58 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2021-03-22 16:46:58 +0100 |
commit | 8ef3b717ccb4f44fc79f44f768bbf6e6da12ff1d (patch) | |
tree | 3d69ae1d953c373e24c04072d86d7a343d2edc7b | |
parent | 81871d3f99f2155b0d81b347b61ac685a7bbc13e (diff) |
Rewrite JAX-RS resources in athenz-identity-provider-service as request handlers
6 files changed, 105 insertions, 155 deletions
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityProviderRequestHandler.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityProviderRequestHandler.java new file mode 100644 index 00000000000..ef98893c0f4 --- /dev/null +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityProviderRequestHandler.java @@ -0,0 +1,101 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.athenz.instanceproviderservice; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.google.inject.Inject; +import com.yahoo.container.jdisc.LoggingRequestHandler; +import com.yahoo.restapi.RestApi; +import com.yahoo.restapi.RestApiException; +import com.yahoo.restapi.RestApiRequestHandler; +import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; +import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; +import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity; +import com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument.IdentityDocumentGenerator; +import com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceConfirmation; +import com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceValidator; + +import java.util.logging.Level; + +/** + * Handler implementing the Athenz Identity Provider API (Copper Argos). + * + * @author bjorncs + */ +public class IdentityProviderRequestHandler extends RestApiRequestHandler<IdentityProviderRequestHandler> { + + private final IdentityDocumentGenerator documentGenerator; + private final InstanceValidator instanceValidator; + + @Inject + public IdentityProviderRequestHandler(LoggingRequestHandler.Context context, + IdentityDocumentGenerator documentGenerator, + InstanceValidator instanceValidator) { + super(context, IdentityProviderRequestHandler::createRestApi); + this.documentGenerator = documentGenerator; + this.instanceValidator = instanceValidator; + } + + private static RestApi createRestApi(IdentityProviderRequestHandler self) { + return RestApi.builder() + .addRoute(RestApi.route("/athenz/v1/provider/identity-document/node/{host}") + .get(self::getNodeIdentityDocument)) + .addRoute(RestApi.route("/athenz/v1/provider/identity-document/tenant/{host}") + .get(self::getTenantIdentityDocument)) + .addRoute(RestApi.route("/athenz/v1/provider/instance") + .post(self::confirmInstance)) + .addRoute(RestApi.route("/athenz/v1/provider/refresh") + .post(self::confirmInstanceRefresh)) + // Overriding object mapper to change serialization of timestamps + .setObjectMapper(new ObjectMapper() + .registerModule(new JavaTimeModule()) + .registerModule(new Jdk8Module()) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true)) + .build(); + } + + private SignedIdentityDocumentEntity getNodeIdentityDocument(RestApi.RequestContext context) { + String host = context.pathParameters().getString("host").orElse(null); + return getIdentityDocument(host, IdentityType.NODE); + } + + private SignedIdentityDocumentEntity getTenantIdentityDocument(RestApi.RequestContext context) { + String host = context.pathParameters().getString("host").orElse(null); + return getIdentityDocument(host, IdentityType.TENANT); + } + + private InstanceConfirmation confirmInstance(RestApi.RequestContext context) { + InstanceConfirmation instanceConfirmation = context.requestContentOrThrow().consumeJacksonEntity(InstanceConfirmation.class); + log.log(Level.FINE, instanceConfirmation.toString()); + if (!instanceValidator.isValidInstance(instanceConfirmation)) { + log.log(Level.SEVERE, "Invalid instance: " + instanceConfirmation); + throw new RestApiException.Forbidden("Instance is invalid"); + } + return instanceConfirmation; + } + + private InstanceConfirmation confirmInstanceRefresh(RestApi.RequestContext context) { + InstanceConfirmation instanceConfirmation = context.requestContentOrThrow().consumeJacksonEntity(InstanceConfirmation.class); + log.log(Level.FINE, instanceConfirmation.toString()); + if (!instanceValidator.isValidRefresh(instanceConfirmation)) { + log.log(Level.SEVERE, "Invalid instance refresh: " + instanceConfirmation); + throw new RestApiException.Forbidden("Instance is invalid"); + } + return instanceConfirmation; + } + + private SignedIdentityDocumentEntity getIdentityDocument(String hostname, IdentityType identityType) { + if (hostname == null) { + throw new RestApiException.BadRequest("The 'hostname' query parameter is missing"); + } + try { + return EntityBindingsMapper.toSignedIdentityDocumentEntity(documentGenerator.generateSignedIdentityDocument(hostname, identityType)); + } catch (Exception e) { + String message = String.format("Unable to generate identity document for '%s': %s", hostname, e.getMessage()); + log.log(Level.SEVERE, message, e); + throw new RestApiException.InternalServerError(message, e); + } + } +} diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java deleted file mode 100644 index cb0b1e0557f..00000000000 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument; - -import com.google.inject.Inject; -import com.yahoo.container.jaxrs.annotation.Component; -import java.util.logging.Level; -import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; -import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; -import com.yahoo.vespa.athenz.identityprovider.api.bindings.IdentityDocumentApi; -import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity; - -import javax.ws.rs.BadRequestException; -import javax.ws.rs.GET; -import javax.ws.rs.InternalServerErrorException; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import java.util.logging.Logger; - -/** - * An API that issues signed identity documents for Vespa nodes. - * - * @author bjorncs - */ -@Path("/identity-document") -public class IdentityDocumentResource implements IdentityDocumentApi { - - private static final Logger log = Logger.getLogger(IdentityDocumentResource.class.getName()); - - private final IdentityDocumentGenerator identityDocumentGenerator; - - @Inject - public IdentityDocumentResource(@Component IdentityDocumentGenerator identityDocumentGenerator) { - this.identityDocumentGenerator = identityDocumentGenerator; - } - - private SignedIdentityDocumentEntity getIdentityDocument(String hostname, IdentityType identityType) { - if (hostname == null) { - throw new BadRequestException("The 'hostname' query parameter is missing"); - } - try { - return EntityBindingsMapper.toSignedIdentityDocumentEntity(identityDocumentGenerator.generateSignedIdentityDocument(hostname, identityType)); - } catch (Exception e) { - String message = String.format("Unable to generate identity doument for '%s': %s", hostname, e.getMessage()); - log.log(Level.SEVERE, message, e); - throw new InternalServerErrorException(message, e); - } - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/node/{host}") - @Override - public SignedIdentityDocumentEntity getNodeIdentityDocument(@PathParam("host") String host) { - return getIdentityDocument(host, IdentityType.NODE); - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/tenant/{host}") - @Override - public SignedIdentityDocumentEntity getTenantIdentityDocument(@PathParam("host") String host) { - return getIdentityDocument(host, IdentityType.TENANT); - } - -} diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java index e6dd40faaca..0c6b5e67a80 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java @@ -13,6 +13,7 @@ import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.yahoo.restapi.RestApi; import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity; import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils; @@ -26,7 +27,7 @@ import java.util.Objects; * * @author bjorncs */ -public class InstanceConfirmation { +public class InstanceConfirmation implements RestApi.JacksonRequestEntity { @JsonProperty("provider") public final String provider; @JsonProperty("domain") public final String domain; diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java deleted file mode 100644 index dbe26d3a6bb..00000000000 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation; - -import com.google.inject.Inject; -import com.yahoo.container.jaxrs.annotation.Component; -import java.util.logging.Level; - -import javax.ws.rs.Consumes; -import javax.ws.rs.ForbiddenException; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import java.util.logging.Logger; - -/** - * @author bjorncs - */ -@Path("/instance") -public class InstanceConfirmationResource { - - private static final Logger log = Logger.getLogger(InstanceConfirmationResource.class.getName()); - - private final InstanceValidator instanceValidator; - - @Inject - public InstanceConfirmationResource(@Component InstanceValidator instanceValidator) { - this.instanceValidator = instanceValidator; - } - - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public InstanceConfirmation confirmInstance(InstanceConfirmation instanceConfirmation) { - log.log(Level.FINE, instanceConfirmation.toString()); - if (!instanceValidator.isValidInstance(instanceConfirmation)) { - log.log(Level.SEVERE, "Invalid instance: " + instanceConfirmation); - throw new ForbiddenException("Instance is invalid"); - } - return instanceConfirmation; - } -} diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceRefreshResource.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceRefreshResource.java deleted file mode 100644 index b49e03658e6..00000000000 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceRefreshResource.java +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation; - -import com.google.inject.Inject; -import com.yahoo.container.jaxrs.annotation.Component; -import java.util.logging.Level; - -import javax.ws.rs.Consumes; -import javax.ws.rs.ForbiddenException; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import java.util.logging.Logger; - -/** - * ZTS calls this resource when it's requested to refresh an instance certificate - * - * @author bjorncs - */ -@Path("/refresh") -public class InstanceRefreshResource { - - private static final Logger log = Logger.getLogger(InstanceRefreshResource.class.getName()); - - private final InstanceValidator instanceValidator; - - @Inject - public InstanceRefreshResource(@Component InstanceValidator instanceValidator) { - this.instanceValidator = instanceValidator; - } - - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public InstanceConfirmation confirmInstanceRefresh(InstanceConfirmation instanceConfirmation) { - log.log(Level.FINE, instanceConfirmation.toString()); - if (!instanceValidator.isValidRefresh(instanceConfirmation)) { - log.log(Level.SEVERE, "Invalid instance refresh: " + instanceConfirmation); - throw new ForbiddenException("Instance is invalid"); - } - return instanceConfirmation; - } -} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java index e6b74d9df4c..9cc21925f52 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.athenz.identityprovider.api.bindings; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.yahoo.restapi.RestApi; import java.time.Instant; import java.util.Objects; @@ -13,7 +14,7 @@ import java.util.Set; * @author bjorncs */ @JsonIgnoreProperties(ignoreUnknown = true) -public class SignedIdentityDocumentEntity { +public class SignedIdentityDocumentEntity implements RestApi.JacksonResponseEntity { @JsonProperty("signature") public final String signature; @JsonProperty("signing-key-version") public final int signingKeyVersion; |