package org.tessell.dispatch.server;
import org.tessell.dispatch.shared.Action;
import org.tessell.dispatch.shared.ActionException;
import org.tessell.dispatch.shared.Result;
/**
* Dispatches actions by first checking the CSRF token.
*
* This prevents CSRF attacks as the payload value (e.g. in the request
* body) must match the secure value (e.g. in the request headers), where
* the secure value is only available to JavaScript on your domain.
*
* See {@link CookieSessionIdValidator} for a default implementation.
*/
public class SessionValidatorDispatch implements ActionDispatch {
private final ActionDispatch delegate;
private final SessionIdValidator validator;
public SessionValidatorDispatch(SessionIdValidator validator, ActionDispatch delegate) {
this.validator = validator;
this.delegate = delegate;
}
@Override
public <A extends Action<R>, R extends Result> R execute(A action, ExecutionContext context) throws ActionException {
validator.setTokenIfNeeded(context);
if (!delegate.skipCSRFCheck(action)) {
String passedSessionId = context.getSessionId(); // from action payload
String secureSessionId = validator.getToken(context); // from header
if (secureSessionId == null || !secureSessionId.equals(passedSessionId)) {
throw invalidSession(context);
}
}
return delegate.execute(action, context);
}
@Override
public boolean skipCSRFCheck(Action<?> action) {
return delegate.skipCSRFCheck(action);
}
/** Allows subclasses to create their own invalid session subclasses of {@link ActionException}. */
protected RuntimeException invalidSession(ExecutionContext context) {
return new IllegalStateException("Invalid session");
}
}