diff options
Diffstat (limited to 'athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityProviderRequestHandler.java')
-rw-r--r-- | athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityProviderRequestHandler.java | 101 |
1 files changed, 101 insertions, 0 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); + } + } +} |