From c174d718de6b5b5e153971aea8b57e514a7f390c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Blanchard?= Date: Tue, 27 Feb 2024 11:49:00 +0100 Subject: [PATCH] QA-14728 The API to retrieve the tools access token is not working properly (#64) * QA-14728 The API to retrieve the tools access token is not working properly * QA-14728; Fix missing file * QA-14728: Fix miss commit file --- .../tools/csrf/MissingTokenException.java | 36 +++++++++++++++++++ .../tools/csrf/ToolsAccessTokenFilter.java | 27 +++++++++----- 2 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/jahia/modules/tools/csrf/MissingTokenException.java diff --git a/src/main/java/org/jahia/modules/tools/csrf/MissingTokenException.java b/src/main/java/org/jahia/modules/tools/csrf/MissingTokenException.java new file mode 100644 index 0000000..67734ea --- /dev/null +++ b/src/main/java/org/jahia/modules/tools/csrf/MissingTokenException.java @@ -0,0 +1,36 @@ +/* + * ========================================================================================== + * = JAHIA'S ENTERPRISE DISTRIBUTION = + * ========================================================================================== + * + * http://www.jahia.com + * + * JAHIA'S ENTERPRISE DISTRIBUTIONS LICENSING - IMPORTANT INFORMATION + * ========================================================================================== + * + * Copyright (C) 2002-2024 Jahia Solutions Group. All rights reserved. + * + * This file is part of a Jahia's Enterprise Distribution. + * + * Jahia's Enterprise Distributions must be used in accordance with the terms + * contained in the Jahia Solutions Group Terms & Conditions as well as + * the Jahia Sustainable Enterprise License (JSEL). + * + * For questions regarding licensing, support, production usage... + * please contact our team at sales@jahia.com or go to http://www.jahia.com/license. + * + * ========================================================================================== + */ +package org.jahia.modules.tools.csrf; + +/** + * Dedicated exception to avoid masquareding of businnes exception inside a protocol one. + * + * @author Jerome Blanchard + */ +public class MissingTokenException extends Exception{ + + public MissingTokenException(String message) { + super(message); + } +} diff --git a/src/main/java/org/jahia/modules/tools/csrf/ToolsAccessTokenFilter.java b/src/main/java/org/jahia/modules/tools/csrf/ToolsAccessTokenFilter.java index d35e2fe..64aa604 100644 --- a/src/main/java/org/jahia/modules/tools/csrf/ToolsAccessTokenFilter.java +++ b/src/main/java/org/jahia/modules/tools/csrf/ToolsAccessTokenFilter.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.UUID; import java.util.regex.Pattern; +import java.util.stream.Collectors; public class ToolsAccessTokenFilter extends AbstractServletFilter { private static final String CSRF_TOKENS_ATTR = "toolAccessTokens"; @@ -45,34 +46,40 @@ public class ToolsAccessTokenFilter extends AbstractServletFilter { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; if (request.getPathInfo() != null && TOOLS_REGEXP.matcher(request.getPathInfo()).matches()) { - if (servletRequest.getParameterMap().size() > 0) { - validateToken(request); + if (!servletRequest.getParameterMap().isEmpty()) { + try { + validateToken(request); + } catch (MissingTokenException e) { + throw new ServletException(e.getMessage()); + } } else { String token = generateAndStoreToken(request); - if (request.getMethod().equals(TOKEN_METHOD) && request.getRequestURI().endsWith(TOKEN_URI)) { HttpServletResponse response = (HttpServletResponse) servletResponse; + String body = "{\"token\":\"" + token + "\"}"; PrintWriter out = response.getWriter(); response.setContentType(TOKEN_CONTENT_TYPE); + response.setContentLength(body.length()); response.setCharacterEncoding(StandardCharsets.UTF_8.name()); - out.print("{\"token\":\"" + token + "\"}"); + response.setStatus(HttpServletResponse.SC_OK); + out.print(body); out.flush(); + return; } } } - filterChain.doFilter(servletRequest, servletResponse); } - @SuppressWarnings("unchecked") - private void validateToken(HttpServletRequest httpReq) throws ServletException { + private void validateToken(HttpServletRequest httpReq) throws MissingTokenException { if (SettingsBean.getInstance().isDevelopmentMode()) { return; } String token = httpReq.getParameter(CSRF_TOKEN_ATTR); if (token == null || getCache(httpReq).get(token) == null || getCache(httpReq).get(token) < (System.currentTimeMillis() - tokenExpiration * 60L * 1000L)) { - throw new ServletException("Missing token: " + httpReq.getRequestURL() + (StringUtils.isNotEmpty(httpReq.getQueryString()) ? ("?" + httpReq.getQueryString()) : "")); + throw new MissingTokenException("Missing token: " + httpReq.getRequestURL() + (StringUtils.isNotEmpty(httpReq.getQueryString()) ? + ("?" + httpReq.getQueryString()) : "")); } // keep same token @@ -86,6 +93,10 @@ private String generateAndStoreToken(HttpServletRequest httpReq) { HashMap tokens = getCache(httpReq); tokens.put(token, System.currentTimeMillis()); + //Purge stale tokens + tokens = tokens.entrySet().stream().filter(e -> e.getValue() > (System.currentTimeMillis() - tokenExpiration * 60L * 1000L)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, HashMap::new)); + if (tokens.size() > MAX_TOKENS) { tokens.remove(tokens.entrySet().stream().min(Map.Entry.comparingByValue()).orElseThrow(ArrayIndexOutOfBoundsException::new).getKey()); }