package io.mangoo.routing.handlers;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import com.google.inject.Inject;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.mangoo.configuration.Config;
import io.mangoo.core.Application;
import io.mangoo.enums.ClaimKey;
import io.mangoo.enums.Required;
import io.mangoo.helpers.RequestHelper;
import io.mangoo.helpers.cookie.CookieBuilder;
import io.mangoo.routing.Attachment;
import io.mangoo.routing.bindings.Authentication;
import io.mangoo.routing.bindings.Flash;
import io.mangoo.routing.bindings.Form;
import io.mangoo.routing.bindings.Session;
import io.mangoo.utils.CodecUtils;
import io.mangoo.utils.DateUtils;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.Cookie;
/**
*
* @author svenkubiak
*
*/
public class OutboundCookiesHandler implements HttpHandler {
private Attachment attachment;
private Config config;
@Inject
public OutboundCookiesHandler(Config config) {
this.config = Objects.requireNonNull(config, Required.CONFIG.toString());
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
this.attachment = exchange.getAttachment(RequestHelper.ATTACHMENT_KEY);
setSessionCookie(exchange);
setFlashCookie(exchange);
setAuthenticationCookie(exchange);
nextHandler(exchange);
}
/**
* Sets the session cookie to the current HttpServerExchange
*
* @param exchange The Undertow HttpServerExchange
*/
protected void setSessionCookie(HttpServerExchange exchange) {
Session session = this.attachment.getSession();
if (session != null && session.hasChanges()) {
Map<String, Object> claims = new HashMap<>();
claims.put(ClaimKey.AUTHENTICITY.toString(), session.getAuthenticity());
claims.put(ClaimKey.VERSION.toString(), this.config.getCookieVersion());
claims.put(ClaimKey.DATA.toString(), session.getValues());
final LocalDateTime expires = session.getExpires();
String jwt = Jwts.builder()
.setClaims(claims)
.setExpiration(DateUtils.localDateTimeToDate(expires))
.signWith(SignatureAlgorithm.HS512, this.config.getApplicationSecret())
.compact();
if (this.config.isSessionCookieEncrypt()) {
jwt = this.attachment.getCrypto().encrypt(jwt);
}
final Cookie cookie = CookieBuilder.create()
.name(this.config.getSessionCookieName())
.value(jwt)
.secure(this.config.isSessionCookieSecure())
.httpOnly(true)
.expires(expires)
.build();
exchange.setResponseCookie(cookie);
}
}
/**
* Sets the authentication cookie to the current HttpServerExchange
*
* @param exchange The Undertow HttpServerExchange
*/
protected void setAuthenticationCookie(HttpServerExchange exchange) {
Authentication authentication = this.attachment.getAuthentication();
if (authentication != null && authentication.hasAuthenticatedUser()) {
Cookie cookie;
final String cookieName = this.config.getAuthenticationCookieName();
if (authentication.isLogout()) {
cookie = exchange.getRequestCookies().get(cookieName);
cookie.setSecure(this.config.isAuthenticationCookieSecure());
cookie.setHttpOnly(true);
cookie.setPath("/");
cookie.setMaxAge(0);
cookie.setDiscard(true);
} else {
Map<String, Object> claims = new HashMap<>();
claims.put(ClaimKey.VERSION.toString(), this.config.getAuthCookieVersion());
claims.put(ClaimKey.TWO_FACTOR.toString(), authentication.isTwoFactor());
final LocalDateTime expires = authentication.isRememberMe() ? LocalDateTime.now().plusHours(this.config.getAuthenticationRememberExpires()) : authentication.getExpires();
String jwt = Jwts.builder()
.setClaims(claims)
.setSubject(authentication.getAuthenticatedUser())
.setExpiration(DateUtils.localDateTimeToDate(expires))
.signWith(SignatureAlgorithm.HS512, this.config.getApplicationSecret())
.compact();
if (this.config.isAuthenticationCookieEncrypt()) {
jwt = this.attachment.getCrypto().encrypt(jwt);
}
cookie = CookieBuilder.create()
.name(cookieName)
.value(jwt)
.secure(this.config.isAuthenticationCookieSecure())
.httpOnly(true)
.expires(expires)
.build();
}
exchange.setResponseCookie(cookie);
}
}
/**
* Sets the flash cookie to current HttpServerExchange
*
* @param exchange The Undertow HttpServerExchange
*/
protected void setFlashCookie(HttpServerExchange exchange) {
Flash flash = this.attachment.getFlash();
Form form = this.attachment.getForm();
if (flash != null && !flash.isDiscard() && (flash.hasContent() || form.flashify())) {
Map<String, Object> claims = new HashMap<>();
claims.put(ClaimKey.DATA.toString(), flash.getValues());
if (form.flashify()) {
claims.put(ClaimKey.FORM.toString(), CodecUtils.serializeToBase64(form));
}
final LocalDateTime expires = LocalDateTime.now().plusSeconds(60);
String jwt = Jwts.builder()
.setClaims(claims)
.setExpiration(DateUtils.localDateTimeToDate(expires))
.signWith(SignatureAlgorithm.HS512, this.config.getApplicationSecret())
.compact();
final Cookie cookie = CookieBuilder.create()
.name(this.config.getFlashCookieName())
.value(jwt)
.secure(this.config.isFlashCookieSecure())
.httpOnly(true)
.expires(expires)
.build();
exchange.setResponseCookie(cookie);
} else {
final Cookie cookie = exchange.getRequestCookies().get(this.config.getFlashCookieName());
if (cookie != null) {
cookie.setHttpOnly(true)
.setSecure(this.config.isFlashCookieSecure())
.setPath("/")
.setMaxAge(0);
exchange.setResponseCookie(cookie);
}
}
}
/**
* Handles the next request in the handler chain
*
* @param exchange The HttpServerExchange
* @throws Exception Thrown when an exception occurs
*/
@SuppressWarnings("all")
protected void nextHandler(HttpServerExchange exchange) throws Exception {
Application.getInstance(ResponseHandler.class).handleRequest(exchange);
}
}