/**
* Copyright 2011-2014 the original author or authors.
*/
package com.jetdrone.vertx.yoke.middleware;
import com.jetdrone.vertx.yoke.Middleware;
import io.vertx.core.http.HttpMethod;
import org.jetbrains.annotations.NotNull;
import io.vertx.core.Handler;
import java.util.UUID;
/**
* # Csrf
*
* This middleware adds a CSRF token to requests which mutate state. You should put the result within a hidden form
* field, query-string etc. This token should be validated against the visitor's session.
* The default value handler checks request body generated by the BodyParser middleware, request query generated and
* the "X-CSRF-Token" header field.
*
* This middleware requires session support, thus should be added somewhere below Session.
*/
public class Csrf extends Middleware {
/**
* Handler that validates the CRSF token
*/
private final ValueHandler valueHandler;
/**
* Name of the property where the token is found/stored in the request context
*/
private final String key;
/**
* Instantiate a new Csrf with a user defined key
*
* <pre>
* new Csrf("_crsf")
* </pre>
*
* @param key name of the context variable to store the token.
*/
public Csrf(@NotNull final String key) {
this.key = key;
valueHandler = new ValueHandler() {
@Override
public String handle(YokeRequest request) {
String token = request.formAttributes().get(key);
if (token == null) {
token = request.params().get(key);
if (token == null) {
token = request.headers().get("x-csrf-token");
}
}
return token;
}
};
}
/**
* Instantiate a new Csrf with the default key "_crsf"
*
* <pre>
* new Csrf()
* </pre>
*/
public Csrf() {
this("_csrf");
}
/**
* Instantiate a new Csrf with custom Handler and key
*
* <pre>
* new Csrf("_crsf", new ValueHandler() {...})
* </pre>
*
* @param key name of the context variable to store the token.
* @param valueHandler the handler for the token validation.
*/
public Csrf(@NotNull String key, @NotNull ValueHandler valueHandler) {
this.key = key;
this.valueHandler = valueHandler;
}
/**
* Instantiate a new Csrf with custom Handler
*
* <pre>
* new Csrf(new ValueHandler() {...})
* </pre>
*
* @param valueHandler the handler for the token validation.
*/
public Csrf(@NotNull final ValueHandler valueHandler) {
this("_csrf", valueHandler);
}
public interface ValueHandler {
String handle(YokeRequest request);
}
@Override
public void handle(@NotNull final YokeRequest request, @NotNull final Handler<Object> next) {
String token = request.get(key);
// generate CSRF token
if (token == null) {
token = UUID.randomUUID().toString();
request.put(key, token);
}
// ignore these methods
if (HttpMethod.GET.equals(request.method()) || HttpMethod.HEAD.equals(request.method()) || HttpMethod.OPTIONS.equals(request.method())) {
next.handle(null);
return;
}
// expect multipart
request.setExpectMultipart(true);
// determine value
String val = valueHandler.handle(request);
// check
if (!token.equals(val)) {
next.handle(403);
return;
}
// OK
next.handle(null);
}
}