All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.trajano.ms.gateway.handlers.AuthenticatedClientValidator Maven / Gradle / Ivy

package net.trajano.ms.gateway.handlers;

import static net.trajano.ms.gateway.internal.MediaTypes.APPLICATION_JSON;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.CorsHandler;
import net.trajano.ms.gateway.internal.Conversions;
import net.trajano.ms.gateway.internal.Errors;
import net.trajano.ms.gateway.internal.MediaTypes;
import net.trajano.ms.gateway.internal.PathContext;
import net.trajano.ms.gateway.providers.GatewayClientAuthorization;

/**
 * 

* Handles determining what Origin URIs are allowed based on the Origin and * Authorization header. If the authorization is Basic, then the client ID and * secret are verified against the system. If it is a Bearer authorization, it * will use the aud value of the data referenced by the access * token. *

* * @author Archimedes Trajano */ @Component @Order(SelfRegisteringRoutingContextHandler.CORE_GLOBAL + 1) public class AuthenticatedClientValidator extends SelfRegisteringRoutingContextHandler { private static final Logger LOG = LoggerFactory.getLogger(AuthenticatedClientValidator.class); /** * Strips off the path components from a URL. * * @param url * URL to process * @return a URI suitable for Access-Control-Allow-Origin * @throws MalformedURLException * invalid URL */ public static URI getPartsForOriginHeader(final URL url) throws MalformedURLException { final String tempOriginString = new URL(url, "/").toString(); return URI.create(tempOriginString.substring(0, tempOriginString.length() - 1)); } @Value("${authorization.endpoint}") private URI authorizationEndpoint; @Autowired private GatewayClientAuthorization gatewayClientAuthorization; @Autowired private HttpClient httpClient; /** * Flag to check if Origin should be checked. */ @Value("${client_validator.require_origin_check:true}") private boolean requireOriginCheck; @Override public void handle(final RoutingContext context) { final PathContext pathContext = getPathContext(context); if (pathContext == null || !pathContext.isProtected()) { context.next(); return; } LOG.debug("Handling {} ended={} path is protected", context, context.request().isEnded()); final String origin = context.request().getHeader(HttpHeaders.ORIGIN); final String referrer = context.request().getHeader(HttpHeaders.REFERER); final URI originUri; try { // Implements a workaround for cordova sending "file://" which is not supported by java.net.URI. if ("file://".equals(origin)) { originUri = URI.create("file:///"); } else { final URL tempOrigin; if (origin == null && referrer != null) { tempOrigin = new URL(referrer); } else { tempOrigin = new URL(origin); } originUri = getPartsForOriginHeader(tempOrigin); } } catch (final MalformedURLException e) { context.response().setStatusCode(400) .putHeader(HttpHeaders.CONTENT_TYPE, MediaTypes.APPLICATION_JSON) .end(Errors.missingOrigin().toBuffer()); return; } LOG.debug("originUri={}", originUri); context.put("origin", originUri); final String authorization = context.request().getHeader(HttpHeaders.AUTHORIZATION); if (authorization == null) { context.next(); return; } LOG.debug("context={} authorization={}", context, authorization); final HttpClientRequest authorizationRequest = httpClient.post(Conversions.toRequestOptions(authorizationEndpoint.resolve("/check")), authorizationResponse -> { LOG.debug("statusCode={}", authorizationResponse.statusCode()); if (authorizationResponse.statusCode() == 204) { context.put("client_authorized", true); context.next(); } else { authorizationResponse.headers().forEach(h -> stripTransferEncodingAndLength(context.response(), h)); authorizationResponse.bodyHandler(body -> { context.response().setStatusCode(authorizationResponse.statusCode()) .setStatusMessage(authorizationResponse.statusMessage()) .setChunked(false) .end(body); LOG.debug("response={}", body); }); } }); final JsonObject checkRequest = new JsonObject(); checkRequest.put("authorization", authorization); checkRequest.put("origin", originUri.toASCIIString()); LOG.debug("checkRequest={}", checkRequest); gatewayClientAuthorization.gatewayRequest(authorizationRequest) .putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) .putHeader(HttpHeaders.ACCEPT, APPLICATION_JSON) .end(checkRequest.toBuffer()); context.request().pause(); } /** * Register this but also register the {@link CorsHandler}. The * {@link CorsHandler} will deal with the normal CORS headers after it has been * processed initially by this handler. {@inheritDoc} */ @Override public void register(final Router router) { router.route().handler(this); if (requireOriginCheck) { router.route().handler(CorsHandler.create(".+") .maxAgeSeconds(600) .allowedMethod(HttpMethod.GET) .allowedMethod(HttpMethod.POST) .allowedMethod(HttpMethod.PUT) .allowedMethod(HttpMethod.DELETE) .allowedMethod(HttpMethod.OPTIONS) .allowedHeader("Content-Type") .allowedHeader("Accept") .allowedHeader("Accept-Language") .allowedHeader("Authorization")); } else { router.route().handler(CorsHandler.create("*") .maxAgeSeconds(600) .allowedMethod(HttpMethod.GET) .allowedMethod(HttpMethod.POST) .allowedMethod(HttpMethod.PUT) .allowedMethod(HttpMethod.DELETE) .allowedMethod(HttpMethod.OPTIONS) .allowedHeader("Content-Type") .allowedHeader("Accept") .allowedHeader("Accept-Language") .allowedHeader("Authorization")); } } private void stripTransferEncodingAndLength(final HttpServerResponse contextResponse, final Map.Entry h) { if ("Content-Length".equalsIgnoreCase(h.getKey()) || "Transfer-Encoding".equalsIgnoreCase(h.getKey())) { return; } contextResponse.putHeader(h.getKey(), h.getValue()); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy