Skip to content

Latest commit

 

History

History
85 lines (79 loc) · 3.66 KB

File metadata and controls

85 lines (79 loc) · 3.66 KB

HTTP Gateway Downstream Validation

This module defines for downstream validation:

  • The validation monad HttpGatewayValidation to help chain validations
  • Common validators using HttpGatewayValidation

Sample usage:

// create validation objects (thread safe)
HttpGatewayClientValidator clientValidator = new HttpGatewayClientValidator(servicesIndex, gatewayClients);
HttpGatewayRouteValidator routeValidator = new HttpGatewayRouteValidator(httpRouter, servicesIndex);
// [...]
// inside HttpGatewayRouterConfiguration.asyncRouting:
// validation
HttpGatewayValidation<HttpGatewayDestinationService> validation = clientValidator
    .validateClientIdentification(downstreamRequest)
    .then(clientId -> routeValidator
        .validate(downstreamRequest)
        .then(destinationRoute -> clientValidator.validateClientAccess(downstreamRequest, destinationRoute, clientId))
    );
// error management
if (validation.isError()) {
    logger.warn(validation.error().getMessage());
    return HttpGatewayDownstreamResponses.buildError(validation.error());
}
HttpGatewayDestinationService destinationService = validation.value();

Custom validators

A complete example showing how to use a custom validator is available in the sample projet, in the custom dimension sample.

To create a custom validator:

  1. Define optionally the class that will contain the resulting data. For example:
@Value
static class CustomClientValidation {
    HttpGatewayDestinationService destinationService; // the route validation result
    CustomAttributes customAttributes; // The custom data resolved with the custom validator
}
  1. Define the validation function that will use the parameters previously resolved. For example:
private static HttpGatewayValidation<CustomClientValidation> validateCustomClientAuthorization(
    Http.Request downstreamRequest,
    CustomClientAttributes clientCustomAttributes,
    HttpGatewayDestinationService destinationService
) {
    // Fetch custom downstream header values
    String[] requestedTenants = downstreamRequest.header(HTTP_HEADER_TENANTS).map(tenants -> tenants.split(",")).orElse(null);
    if (requestedTenants == null) {
        return HttpGatewayValidation.ofError(HttpResponseStatus.PRECONDITION_FAILED, "Missing header " + HTTP_HEADER_TENANTS);
    }

    // Verify client access to the requested values
    for (String requestedTenant : requestedTenants) {
        if (!clientCustomAttributes.getTenants().contains(requestedTenant)) {
            return HttpGatewayValidation.ofError(HttpResponseStatus.UNAUTHORIZED, "Access to tenant '" + requestedTenant + "' is not allowed. Allowed tenants are: " + clientCustomAttributes.getTenants());
        }
    }

    // return success validation with the custom value
    return HttpGatewayValidation.ofValue(new CustomClientValidation(
        destinationService,
        new CustomClientAttributes(Set.of(requestedTenants))
    ));
}
  1. Use the custom validator:
// custom validation
Map<String, CustomClientAttributes> clientsCustomAttributes = readClientsCustomAttributes(configLoader);

// [...]
HttpGatewayValidation<CustomClientValidation> validation = clientValidator
    .validateClientIdentification(downstreamRequest)
    .then(clientId -> routeValidator
        .validate(downstreamRequest)
        .then(destinationRoute -> clientValidator.validateClientAccess(downstreamRequest, destinationRoute, clientId))
        // custom validation
        .then(destinationService -> validateCustomClientAuthorization(
            downstreamRequest, clientsCustomAttributes.get(clientId), destinationService
        ))
    );