Skip to content

Commit

Permalink
Implement safe routes PoC
Browse files Browse the repository at this point in the history
Signed-off-by: Paulo Lopes <[email protected]>
  • Loading branch information
pmlopes committed Apr 1, 2022
1 parent 8ed7041 commit e594d92
Show file tree
Hide file tree
Showing 20 changed files with 145 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.vertx.ext.web.api.contract.impl.BaseRouterFactory;
import io.vertx.ext.web.api.contract.impl.RouteToEBServiceHandler;
import io.vertx.ext.web.api.contract.openapi3.OpenAPI3RouterFactory;
import io.vertx.ext.web.handler.PlatformHandler;
import io.vertx.ext.web.handler.ResponseContentTypeHandler;
import io.vertx.ext.web.impl.RouteImpl;

Expand Down Expand Up @@ -379,7 +380,8 @@ public Router getRouter() {

String exposeConfigurationKey = this.getOptions().getOperationModelKey();
if (exposeConfigurationKey != null)
route.handler(context -> context.put(exposeConfigurationKey, operation.getOperationModel()).next());
route.handler(
(PlatformHandler) context -> context.put(exposeConfigurationKey, operation.getOperationModel()).next());

// Set produces/consumes
Set<String> consumes = new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ private Handler<RoutingContext> generateFailureHandler(boolean expected) {
}

private void startServer() throws InterruptedException {
router = routerFactory.getRouter();
try {
router = routerFactory.getRouter();
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
}
server = vertx.createHttpServer(new HttpServerOptions().setPort(8080).setHost("localhost"));
CountDownLatch latch = new CountDownLatch(1);
server.requestHandler(router).listen(onSuccess(res -> latch.countDown()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.AuthenticationHandler;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.PlatformHandler;
import io.vertx.ext.web.handler.ResponseContentTypeHandler;
import io.vertx.ext.web.impl.RouteImpl;
import io.vertx.ext.web.openapi.*;
Expand Down Expand Up @@ -352,7 +353,7 @@ public Router createRouter() {

String exposeConfigurationKey = this.getOptions().getOperationModelKey();
if (exposeConfigurationKey != null)
route.handler(context -> context.put(exposeConfigurationKey, operation.getOperationModel()).next());
route.handler((PlatformHandler) context -> context.put(exposeConfigurationKey, operation.getOperationModel()).next());

// Set produces/consumes
Set<String> consumes = ((JsonObject) JsonPointer.from("/requestBody/content")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ protected Future<Void> startServer(Vertx vertx, RouterBuilder factory,
try {
router = factory.createRouter();
} catch (Throwable e) {
e.printStackTrace();
return Future.failedFuture(e);
}
ValidationTestUtils.mountRouterFailureHandler(router);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import io.vertx.codegen.annotations.Fluent;
import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.impl.BodyHandlerImpl;

Expand All @@ -30,7 +29,7 @@
* @author <a href="http://tfox.org">Tim Fox</a>
*/
@VertxGen
public interface BodyHandler extends Handler<RoutingContext> {
public interface BodyHandler extends PlatformHandler {

/**
* Default max size for a request body = {@code -1} means unlimited
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
* implement it, and vice-versa: browsers that don't support CSP simply ignore it, functioning as usual, defaulting to
* the standard same-origin policy for web content. If the site doesn't offer the CSP header, browsers likewise use the
* standard same-origin policy.
*
* @author <a href="mailto:[email protected]">Paulo Lopes</a>
*/
@VertxGen
public interface CSPHandler extends Handler<RoutingContext> {
public interface CSPHandler extends SecurityPolicyHandler {

/**
* Creates a new instance of the handler.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*
* This Handler requires session support, thus should be added somewhere below Session and Body handlers.
*
* @author Paulo Lopes
* @author <a href="mailto:[email protected]">Paulo Lopes</a>
*/
@VertxGen
public interface CSRFHandler extends Handler<RoutingContext> {
public interface CSRFHandler extends SecurityPolicyHandler {

String DEFAULT_COOKIE_NAME = "XSRF-TOKEN";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@
* A handler which implements server side http://www.w3.org/TR/cors/[CORS] support for Vert.x-Web.
*
* @author <a href="http://tfox.org">Tim Fox</a>
* @author <a href="mailto:[email protected]">Paulo Lopes</a>
*/
@VertxGen
public interface CorsHandler extends Handler<RoutingContext> {
public interface CorsHandler extends SecurityPolicyHandler {

/**
* Create a CORS handler using a regular expression to match origins. An origin follows rfc6454#section-7
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@

import io.vertx.codegen.annotations.Fluent;
import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Handler;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.web.handler.impl.FormLoginHandlerImpl;
import io.vertx.ext.web.RoutingContext;

/**
* Handler that handles login from a form on a custom login page.
Expand All @@ -31,7 +29,7 @@
* @author <a href="http://tfox.org">Tim Fox</a>
*/
@VertxGen
public interface FormLoginHandler extends Handler<RoutingContext> {
public interface FormLoginHandler extends AuthenticationHandler {

/**
* The default value of the form attribute which will contain the username
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
* HTTP Strict Transport Security (HSTS) <a href="http://tools.ietf.org/html/rfc6797">RFC6797</a>.
*
* This handler adds the strict transport security headers, for this domain or subdomains.
*
* @author <a href="mailto:[email protected]">Paulo Lopes</a>
*/
@VertxGen
public interface HSTSHandler extends Handler<RoutingContext> {
public interface HSTSHandler extends SecurityPolicyHandler {

// 6 months
long DEFAULT_MAX_AGE = 15768000;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
/**
* An auth handler that provides One Time Password (Multi-Factor) Authentication support.
*
* @author Paulo Lopes
* @author <a href="mailto:[email protected]">Paulo Lopes</a>
*/
@VertxGen
public interface OtpAuthHandler extends Handler<RoutingContext> {
public interface OtpAuthHandler extends AuthenticationHandler {

/**
* Create a new instance of this handler using a time based one time password authentication provider.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2021 Red Hat, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.ext.web.handler;

import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

/**
* Base platform interface for handlers that provide functionality to the application platform.
*
* Two examples are:
*
* <ul>
* <li>{@link BodyHandler}</li>
* <li>{@link SessionHandler}</li>
* </ul>
*
* @author <a href="mailto:[email protected]">Paulo Lopes</a>
*/
@VertxGen(concrete = false)
public interface PlatformHandler extends Handler<RoutingContext> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
* @see RoutingContext#getAcceptableContentType()
*/
@VertxGen
public interface ResponseContentTypeHandler extends Handler<RoutingContext> {
public interface ResponseContentTypeHandler extends PlatformHandler {

String DEFAULT_DISABLE_FLAG = "__vertx.autoContenType.disable";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2021 Red Hat, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.ext.web.handler;

import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

/**
* Base security policy interface for handlers that provide HTTP security related headers.
* <p>
* Sub-interfaces help you secure your applications by setting various HTTP headers. <i>It's not a silver bullet</i>,
* but it can help!
* <p>
*
* @author <a href="mailto:[email protected]">Paulo Lopes</a>
*/
@VertxGen(concrete = false)
public interface SecurityPolicyHandler extends Handler<RoutingContext> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
* @author <a href="http://tfox.org">Tim Fox</a>
*/
@VertxGen
public interface SessionHandler extends Handler<RoutingContext> {
public interface SessionHandler extends PlatformHandler {

/**
* Default name of session cookie
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
package io.vertx.ext.web.handler;

import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

/**
* The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render
Expand All @@ -27,10 +25,10 @@
* The added security is provided only if the user accessing the document is using a browser that supports
* {@code X-Frame-Options}.
*
* @author Paulo Lopes
* @author <a href="mailto:[email protected]">Paulo Lopes</a>
*/
@VertxGen
public interface XFrameHandler extends Handler<RoutingContext> {
public interface XFrameHandler extends SecurityPolicyHandler {

/**
* The page cannot be displayed in a frame, regardless of the site attempting to do so.
Expand Down
39 changes: 39 additions & 0 deletions vertx-web/src/main/java/io/vertx/ext/web/impl/RouteState.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
import io.vertx.core.net.impl.URIDecoder;
import io.vertx.ext.web.MIMEHeader;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.AuthenticationHandler;
import io.vertx.ext.web.handler.AuthorizationHandler;
import io.vertx.ext.web.handler.PlatformHandler;
import io.vertx.ext.web.handler.SecurityPolicyHandler;

import java.util.*;
import java.util.regex.Matcher;
Expand All @@ -35,6 +39,31 @@
*/
final class RouteState {

enum Priority {
PLATFORM,
SECURITY_POLICY,
AUTHENTICATION,
AUTHORIZATION,
USER
}

private static Priority weight(Handler<RoutingContext> handler) {
if (handler instanceof PlatformHandler) {
return Priority.PLATFORM;
}
if (handler instanceof SecurityPolicyHandler) {
return Priority.SECURITY_POLICY;
}
if (handler instanceof AuthenticationHandler) {
return Priority.AUTHENTICATION;
}
if (handler instanceof AuthorizationHandler) {
return Priority.AUTHORIZATION;
}

return Priority.USER;
}

private final RouteImpl route;

private final Map<String, Object> metadata;
Expand Down Expand Up @@ -495,6 +524,15 @@ RouteState addContextHandler(Handler<RoutingContext> contextHandler) {
this.exclusive,
this.exactPath);

final Priority weight = weight(contextHandler);

for (int i = 0; i < newState.contextHandlers.size(); i++) {
Priority iterWeith = weight(newState.contextHandlers.get(i));
if (iterWeith.ordinal() > weight.ordinal()) {
throw new IllegalStateException("Cannot add [" + weight.name() + "] handler to route with [" + iterWeith.name() + "] handler at index " + i);
}
}

newState.contextHandlers.add(contextHandler);
return newState;
}
Expand Down Expand Up @@ -900,6 +938,7 @@ RouteState setName(String name) {
this.exclusive,
this.exactPath);
}

private boolean containsMethod(HttpServerRequest request) {
if (!isEmpty(methods)) {
return methods.contains(request.method());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,18 @@ final class RouterState {
// we keep a set of handlers ordered by its "order" property
final int compare = Integer.compare(o1.order(), o2.order());
// since we are defining the comparator to order the set we must be careful because the set
// will use the comparator to compare the identify of the handlers and if they are the same order
// will use the comparator to compare the identity of the handlers and if they are the same order
// are assumed to be the same comparator and therefore removed from the set.

// if the 2 routes being compared by its order have the same order property value,
// then do a more expensive equality check and if and only if the are the same we
// then do a more expensive equality check and if and only if they are the same we
// do return 0, meaning same order and same identity.
if (compare == 0) {
if (o1.equals(o2)) {
return 0;
}
// otherwise we return higher so if 2 routes have the same order the second one will be considered
// higher so it is added after the first.
// otherwise, we return higher so if 2 routes have the same order the second one will be considered
// higher, so it is added after the first.
return 1;
}
return compare;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.vertx.core.http.impl.HttpUtils;
import io.vertx.core.impl.ContextInternal;
import io.vertx.ext.auth.User;
import io.vertx.ext.web.RequestBody;
import io.vertx.ext.web.FileUpload;
import io.vertx.ext.web.RequestBody;
import io.vertx.ext.web.RoutingContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public void testSockJSInternalCORSHandling() {
public void testNoConflictsSockJSAndCORSHandler() {
router
.route()
.handler(CorsHandler.create("*").allowCredentials(false))
.handler(BodyHandler.create());
.handler(BodyHandler.create())
.handler(CorsHandler.create("*").allowCredentials(false));
SockJSProtocolTest.installTestApplications(router, vertx);
client.request(HttpMethod.GET, "/echo/info?t=21321")
.compose(req -> req
Expand Down

0 comments on commit e594d92

Please sign in to comment.