diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java index e25c60673e..18fde20361 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java @@ -109,6 +109,8 @@ public abstract class CBApplication extends private final Map initActions = new ConcurrentHashMap<>(); + private CBJettyServer jettyServer; + public CBApplication() { this.homeDirectory = new File(initHomeFolder()); } @@ -465,7 +467,8 @@ public abstract class CBApplication extends getServerPort(), CommonUtils.isEmpty(getServerHost()) ? "all interfaces" : getServerHost()) ); - new CBJettyServer(this).runServer(); + this.jettyServer = new CBJettyServer(this); + this.jettyServer.runServer(); } @@ -565,6 +568,9 @@ public abstract class CBApplication extends sendConfigChangedEvent(credentialsProvider); eventController.setForceSkipEvents(isConfigurationMode()); + if (this.jettyServer != null) { + this.jettyServer.refreshJettyConfig(); + } } protected abstract void finishSecurityServiceConfiguration( diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java index 7cee228b18..4c7cfde0cc 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java @@ -61,6 +61,7 @@ public class CBJettyServer { } private final CBApplication application; + private Server server; public CBJettyServer(@NotNull CBApplication application) { this.application = application; @@ -69,7 +70,6 @@ public class CBJettyServer { public void runServer() { try { CBServerConfig serverConfiguration = application.getServerConfiguration(); - Server server; int serverPort = serverConfiguration.getServerPort(); String serverHost = serverConfiguration.getServerHost(); Path sslPath = getSslConfigurationPath(); @@ -198,7 +198,7 @@ public class CBJettyServer { } } } - + refreshJettyConfig(); server.start(); server.join(); } catch (Exception e) { @@ -224,6 +224,7 @@ public class CBJettyServer { ) { // Init sessions persistence CBSessionHandler sessionHandler = new CBSessionHandler(application); + sessionHandler.setRefreshCookieAge(CBSessionHandler.ONE_MINUTE); int intMaxIdleSeconds; if (maxIdleTime > Integer.MAX_VALUE) { log.warn("Max session idle time value is greater than Integer.MAX_VALUE. Integer.MAX_VALUE will be used instead"); @@ -232,6 +233,7 @@ public class CBJettyServer { intMaxIdleSeconds = (int) (maxIdleTime / 1000); log.debug("Max http session idle time: " + intMaxIdleSeconds + "s"); sessionHandler.setMaxInactiveInterval(intMaxIdleSeconds); + sessionHandler.setMaxCookieAge(intMaxIdleSeconds); DefaultSessionCache sessionCache = new DefaultSessionCache(sessionHandler); sessionCache.setSessionDataStore(new NullSessionDataStore()); @@ -241,6 +243,19 @@ public class CBJettyServer { DefaultSessionIdManager idMgr = new DefaultSessionIdManager(server); idMgr.setWorkerName(null); server.addBean(idMgr, true); + } + public synchronized void refreshJettyConfig() { + if (server == null) { + return; + } + log.info("Refreshing Jetty configuration"); + if (server.getHandler() instanceof ServletContextHandler servletContextHandler + && servletContextHandler.getSessionHandler() instanceof CBSessionHandler cbSessionHandler + ) { + cbSessionHandler.setMaxCookieAge((int) (application.getMaxSessionIdleTime() / 1000)); + var serverUrl = this.application.getServerURL(); + cbSessionHandler.setSecureCookies(serverUrl != null && serverUrl.startsWith("https://")); + } } } \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBSessionHandler.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBSessionHandler.java index 6fa6d0f7b8..b8539264f7 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBSessionHandler.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBSessionHandler.java @@ -17,174 +17,13 @@ package io.cloudbeaver.server.jetty; import io.cloudbeaver.server.GQLApplicationAdapter; -import jakarta.servlet.SessionCookieConfig; -import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.SessionHandler; -import java.util.Collections; -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; - public class CBSessionHandler extends SessionHandler { - private final CBCookieConfig cbCookieConfig; + static final int ONE_MINUTE = 60; private final GQLApplicationAdapter application; public CBSessionHandler(GQLApplicationAdapter application) { - this.cbCookieConfig = new CBCookieConfig(); this.application = application; } - - - @Override - public SessionCookieConfig getSessionCookieConfig() { - return this.cbCookieConfig; - } - - - //mostly copy of org.eclipse.jetty.ee10.servlet.CookieConfig but allows to use dynamic setSecure flag - public final class CBCookieConfig implements SessionCookieConfig { - - @Override - public boolean isSecure() { - var serverUrl = CBSessionHandler.this.application.getServerURL(); - return serverUrl != null && serverUrl.startsWith("https://"); - } - - @Override - public String getComment() { - return getSessionComment(); - } - - @Override - public String getDomain() { - return getSessionDomain(); - } - - @Override - public int getMaxAge() { - return getMaxCookieAge(); - } - - @Override - public void setAttribute(String name, String value) { - checkState(); - String lcase = name.toLowerCase(Locale.ENGLISH); - - switch (lcase) { - case "name" -> setName(value); - case "max-age" -> setMaxAge(value == null ? -1 : Integer.parseInt(value)); - case "comment" -> setComment(value); - case "domain" -> setDomain(value); - case "httponly" -> setHttpOnly(Boolean.parseBoolean(value)); - case "secure" -> setSecure(Boolean.parseBoolean(value)); - case "path" -> setPath(value); - default -> setSessionCookieAttribute(name, value); - } - } - - @Override - public String getAttribute(String name) { - String lcase = name.toLowerCase(Locale.ENGLISH); - return switch (lcase) { - case "name" -> getName(); - case "max-age" -> Integer.toString(getMaxAge()); - case "comment" -> getComment(); - case "domain" -> getDomain(); - case "httponly" -> String.valueOf(isHttpOnly()); - case "secure" -> String.valueOf(isSecure()); - case "path" -> getPath(); - default -> getSessionCookieAttribute(name); - }; - } - - /** - * According to the SessionCookieConfig javadoc, the attributes must also include - * all values set by explicit setters. - * - * @see SessionCookieConfig - */ - @Override - public Map getAttributes() { - Map specials = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - specials.put("name", getAttribute("name")); - specials.put("max-age", getAttribute("max-age")); - specials.put("comment", getAttribute("comment")); - specials.put("domain", getAttribute("domain")); - specials.put("httponly", getAttribute("httponly")); - specials.put("secure", getAttribute("secure")); - specials.put("path", getAttribute("path")); - specials.putAll(getSessionCookieAttributes()); - return Collections.unmodifiableMap(specials); - } - - @Override - public String getName() { - return getSessionCookie(); - } - - @Override - public String getPath() { - return getSessionPath(); - } - - @Override - public boolean isHttpOnly() { - return CBSessionHandler.this.isHttpOnly(); - } - - @Override - public void setComment(String comment) { - checkState(); - CBSessionHandler.this.setSessionComment(comment); - } - - @Override - public void setDomain(String domain) { - checkState(); - CBSessionHandler.this.setSessionDomain(domain); - } - - @Override - public void setHttpOnly(boolean httpOnly) { - checkState(); - CBSessionHandler.this.setHttpOnly(httpOnly); - } - - @Override - public void setMaxAge(int maxAge) { - checkState(); - CBSessionHandler.this.setMaxCookieAge(maxAge); - } - - @Override - public void setName(String name) { - checkState(); - CBSessionHandler.this.setSessionCookie(name); - } - - @Override - public void setPath(String path) { - checkState(); - CBSessionHandler.this.setSessionPath(path); - } - - @Override - public void setSecure(boolean secure) { - checkState(); - CBSessionHandler.this.setSecureCookies(secure); - } - - private void checkState() { - //It is allowable to call the CookieConfig.setXX methods after the SessionHandler has started, - //but before the context has fully started. Ie it is allowable for ServletContextListeners - //to call these methods in contextInitialized(). - ServletContextHandler handler = ServletContextHandler.getCurrentServletContextHandler(); - if (handler != null && handler.isAvailable()) - throw new IllegalStateException("CookieConfig cannot be set after ServletContext is started"); - - } - } - - }