mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-5615 dynamic cookie lifetime (#2956)
* CB-5615 dynamic cookie lifetime
This commit is contained in:
@@ -109,6 +109,8 @@ public abstract class CBApplication<T extends CBServerConfig> extends
|
||||
|
||||
private final Map<String, String> initActions = new ConcurrentHashMap<>();
|
||||
|
||||
private CBJettyServer jettyServer;
|
||||
|
||||
public CBApplication() {
|
||||
this.homeDirectory = new File(initHomeFolder());
|
||||
}
|
||||
@@ -465,7 +467,8 @@ public abstract class CBApplication<T extends CBServerConfig> 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<T extends CBServerConfig> extends
|
||||
|
||||
sendConfigChangedEvent(credentialsProvider);
|
||||
eventController.setForceSkipEvents(isConfigurationMode());
|
||||
if (this.jettyServer != null) {
|
||||
this.jettyServer.refreshJettyConfig();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void finishSecurityServiceConfiguration(
|
||||
|
||||
+17
-2
@@ -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://"));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-162
@@ -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<String, String> getAttributes() {
|
||||
Map<String, String> 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");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user