Skip to content

Commit

Permalink
Merge branch 'devel' into CB-5954-ldap-configuration
Browse files Browse the repository at this point in the history
# Conflicts:
#	server/bundles/io.cloudbeaver.service.ldap.auth/OSGI-INF/l10n/bundle_ru.properties
  • Loading branch information
DenisSinelnikov committed Dec 24, 2024
2 parents 49dbfb7 + 449b66f commit 0b81f9f
Show file tree
Hide file tree
Showing 22 changed files with 356 additions and 300 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
[Database access instructions](https://github.com/dbeaver/cloudbeaver/wiki/Demo-Server)

## Changelog

### 24.3.1. 2024-12-23
- Added an ability to reconnect for all editors if a connection has been lost;
- Added an option to replace line break characters on any custom value when exporting to CSV;
- Added an ability to create connections in the Navigation tree not only on the initial level but in folders and sub-folders directly;
- Updated list of available shortcuts for MacOS.

### 24.3.0. 2024-12-02
### Changes since 24.2.0:
- General:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,33 @@
package io.cloudbeaver.model.session;

import jakarta.servlet.http.HttpServletRequest;
import org.jkiss.code.Nullable;

public class WebHttpRequestInfo {
public static final String USER_AGENT = "User-Agent";

@Nullable
private final String id;
@Nullable
private final Object locale;
@Nullable
private final String lastRemoteAddress;
@Nullable
private final String lastRemoteUserAgent;

public WebHttpRequestInfo(HttpServletRequest request) {
this.id = request.getSession().getId();
this.id = request.getSession() == null ? null : request.getSession().getId();
this.locale = request.getAttribute("locale");
this.lastRemoteAddress = request.getRemoteAddr();
this.lastRemoteUserAgent = request.getHeader("User-Agent");
this.lastRemoteUserAgent = request.getHeader(USER_AGENT);
}

public WebHttpRequestInfo(String id, Object locale, String lastRemoteAddress, String lastRemoteUserAgent) {
public WebHttpRequestInfo(
@Nullable String id,
@Nullable Object locale,
@Nullable String lastRemoteAddress,
@Nullable String lastRemoteUserAgent
) {
this.id = id;
this.locale = locale;
this.lastRemoteAddress = lastRemoteAddress;
Expand All @@ -43,14 +54,17 @@ public String getId() {
return id;
}

@Nullable
public Object getLocale() {
return locale;
}

@Nullable
public String getLastRemoteAddress() {
return lastRemoteAddress;
}

@Nullable
public String getLastRemoteUserAgent() {
return lastRemoteUserAgent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import io.cloudbeaver.server.jobs.SessionStateJob;
import io.cloudbeaver.server.jobs.WebDataSourceMonitorJob;
import io.cloudbeaver.server.jobs.WebSessionMonitorJob;
import io.cloudbeaver.service.session.CBSessionManager;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.jkiss.code.NotNull;
Expand Down Expand Up @@ -84,6 +83,7 @@ protected synchronized void initialize() {
}

protected void scheduleServerJobs() {
super.scheduleServerJobs();
new WebSessionMonitorJob(this, application.getSessionManager())
.scheduleMonitor();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,22 @@
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.WebApplication;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.servlets.CBImageServlet;
import io.cloudbeaver.server.servlets.CBStaticServlet;
import io.cloudbeaver.server.servlets.WebStatusServlet;
import io.cloudbeaver.server.websockets.CBJettyWebSocketManager;
import io.cloudbeaver.server.websockets.CBEventsWebSocket;
import io.cloudbeaver.server.websockets.CBWebSocketServerConfigurator;
import io.cloudbeaver.service.DBWServiceBindingServlet;
import io.cloudbeaver.service.DBWServiceBindingWebSocket;
import jakarta.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.session.DefaultSessionCache;
import org.eclipse.jetty.session.DefaultSessionIdManager;
import org.eclipse.jetty.session.NullSessionDataStore;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
Expand All @@ -48,7 +46,6 @@
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Arrays;

public class CBJettyServer {
Expand Down Expand Up @@ -137,7 +134,7 @@ public void runServer() {
}

CBJettyWebSocketContext webSocketContext = new CBJettyWebSocketContext(server, servletContextHandler);
for (DBWServiceBindingWebSocket<CBApplication> wsb : WebServiceRegistry.getInstance()
for (DBWServiceBindingWebSocket wsb : WebServiceRegistry.getInstance()
.getWebServices(DBWServiceBindingWebSocket.class)
) {
if (wsb.isApplicable(this.application)) {
Expand All @@ -149,16 +146,16 @@ public void runServer() {
}
}

WebSocketUpgradeHandler webSocketHandler = WebSocketUpgradeHandler.from(server, servletContextHandler, (wsContainer) -> {
wsContainer.setIdleTimeout(Duration.ofMinutes(5));
// Add websockets
wsContainer.addMapping(
serverConfiguration.getServicesURI() + "ws",
new CBJettyWebSocketManager(this.application.getSessionManager())
);
}
);
servletContextHandler.insertHandler(webSocketHandler);
JakartaWebSocketServletContainerInitializer.configure(servletContextHandler, (context, container) -> {
// Add echo endpoint to server container
ServerEndpointConfig eventWsEnpoint = ServerEndpointConfig.Builder
.create(
CBEventsWebSocket.class,
serverConfiguration.getServicesURI() + "ws"
).configurator(new CBWebSocketServerConfigurator(application.getSessionManager()))
.build();
container.addEndpoint(eventWsEnpoint);
});

JettyUtils.initSessionManager(
this.application.getMaxSessionIdleTime(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import io.cloudbeaver.registry.WebHandlerRegistry;
import io.cloudbeaver.registry.WebServletHandlerDescriptor;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
Expand All @@ -49,7 +48,6 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
Expand Down Expand Up @@ -227,7 +225,7 @@ private void patchStaticContentIfNeeded(HttpServletRequest request, HttpServletR
response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "no-cache, no-store, must-revalidate");
response.setHeader(HttpHeader.CONTENT_TYPE.toString(), MimeTypes.TEXT_HTML);
response.setHeader(HttpHeader.EXPIRES.toString(), "0");
response.getOutputStream().write(ByteBuffer.wrap(indexBytes));
response.getOutputStream().write(indexBytes);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,18 @@
import io.cloudbeaver.registry.WebHandlerRegistry;
import io.cloudbeaver.registry.WebSessionHandlerDescriptor;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.WebAppSessionManager;
import io.cloudbeaver.server.events.WSWebUtils;
import io.cloudbeaver.service.DBWSessionHandler;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Session;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMAuthInfo;
import org.jkiss.dbeaver.model.security.user.SMAuthPermissions;
import org.jkiss.dbeaver.model.websocket.WSConstants;
import org.jkiss.dbeaver.model.websocket.event.WSUserDeletedEvent;
import org.jkiss.dbeaver.model.websocket.event.session.WSSessionStateEvent;
import org.jkiss.utils.CommonUtils;
Expand Down Expand Up @@ -163,15 +159,12 @@ public WebSession getWebSession(
* @return WebSession object or null, if session expired or invalid
*/
@Nullable
public WebSession getOrRestoreSession(@NotNull Request request) {
var sessionIdCookie = Request.getCookies(request).stream().filter(
c -> c.getName().equals(CBConstants.CB_SESSION_COOKIE_NAME)
).findAny().orElse(null);
if (sessionIdCookie == null) {
public WebSession getOrRestoreWebSession(@NotNull WebHttpRequestInfo requestInfo) {
final var sessionId = requestInfo.getId();
if (sessionId == null) {
log.debug("Http session is null. No Web Session returned");
return null;
}
var sessionId = sessionIdCookie.getValue();
WebSession webSession;
synchronized (sessionMap) {
if (sessionMap.containsKey(sessionId)) {
Expand All @@ -189,12 +182,7 @@ public WebSession getOrRestoreSession(@NotNull Request request) {
return null;
}

webSession = createWebSessionImpl(new WebHttpRequestInfo(
request.getId(),
request.getAttribute("locale"),
Request.getRemoteAddr(request),
request.getHeaders().get("User-Agent")
));
webSession = createWebSessionImpl(requestInfo);
restorePreviousUserSession(webSession, oldAuthInfo);

sessionMap.put(sessionId, webSession);
Expand Down Expand Up @@ -301,15 +289,18 @@ public Collection<BaseWebSession> getAllActiveSessions() {
}

@Nullable
public WebHeadlessSession getHeadlessSession(Request request, Session session, boolean create) throws DBException {
String smAccessToken = request.getHeaders().get(WSConstants.WS_AUTH_HEADER);
public WebHeadlessSession getHeadlessSession(
@Nullable String smAccessToken,
@NotNull WebHttpRequestInfo requestInfo,
boolean create
) throws DBException {
if (CommonUtils.isEmpty(smAccessToken)) {
return null;
}
synchronized (sessionMap) {
var tempCredProvider = new SMTokenCredentialProvider(smAccessToken);
SMAuthPermissions authPermissions = application.createSecurityController(tempCredProvider).getTokenPermissions();
var sessionId = session != null ? session.getId()
var sessionId = requestInfo.getId() != null ? requestInfo.getId()
: authPermissions.getSessionId();

var existSession = sessionMap.get(sessionId);
Expand Down
2 changes: 1 addition & 1 deletion server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
org.jkiss.utils;visibility:=reexport,
org.jkiss.dbeaver.model.sql;visibility:=reexport,
org.jkiss.dbeaver.data.gis,
org.jkiss.bundle.jetty.server;visibility:=reexport,
org.jkiss.bundle.jakarta.jetty.server;visibility:=reexport,
com.google.gson;visibility:=reexport,
org.jkiss.bundle.graphql.java;visibility:=reexport,
org.jkiss.bundle.apache.dbcp,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
log.api.graphql.debug = \u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u043E\u0435 \u043B\u043E\u0433\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435
log.api.graphql.debug.description = \u0412\u043A\u043B\u044E\u0447\u0438\u0442\u0435 \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u043E\u0435 \u043B\u043E\u0433\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432 GraphQL \u0432 \u0436\u0443\u0440\u043D\u0430\u043B\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u0432\u043A\u043B\u044E\u0447\u0430\u044F \u0432\u0441\u0435 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435
log.api.graphql.debug = Включить подробное логирование
log.api.graphql.debug.description = Включите подробное логирование запросов GraphQL в журнале сервера, включая все предоставленные переменные
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.cloudbeaver.server;

import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.server.websockets.WebSocketPingPongJob;
import org.eclipse.core.runtime.Plugin;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
Expand Down Expand Up @@ -132,7 +133,9 @@ private void initTempFolder(@NotNull DBRProgressMonitor monitor) {
@NotNull
public abstract WebApplication getApplication();

protected abstract void scheduleServerJobs();
protected void scheduleServerJobs() {
new WebSocketPingPongJob(WebAppUtils.getWebPlatform()).scheduleMonitor();
}

@Override
public synchronized void dispose() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebHeadlessSession;
import io.cloudbeaver.model.session.WebHttpRequestInfo;
import io.cloudbeaver.model.session.WebSession;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Session;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
Expand Down Expand Up @@ -56,9 +55,13 @@ WebSession getWebSession(

Collection<BaseWebSession> getAllActiveSessions();

WebSession getOrRestoreSession(Request httpRequest);
WebSession getOrRestoreWebSession(WebHttpRequestInfo httpRequest);

WebHeadlessSession getHeadlessSession(Request request, Session session, boolean create) throws DBException;
WebHeadlessSession getHeadlessSession(
@Nullable String smAccessToken,
@NotNull WebHttpRequestInfo requestInfo,
boolean create
) throws DBException;

boolean touchSession(HttpServletRequest request, HttpServletResponse response) throws DBWebException;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,34 @@
package io.cloudbeaver.server.jetty;

import io.cloudbeaver.service.DBWWebSocketContext;
import jakarta.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.websocket.api.Configurable;
import org.eclipse.jetty.websocket.server.WebSocketCreator;
import org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler;
import org.jkiss.code.NotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

public class CBJettyWebSocketContext implements DBWWebSocketContext {
private final List<String> mappings = new ArrayList<>();

private final Server server;
private final ContextHandler handler;
private final ServletContextHandler servletContextHandler;

public CBJettyWebSocketContext(@NotNull Server server, @NotNull ContextHandler handler) {
public CBJettyWebSocketContext(@NotNull Server server, @NotNull ServletContextHandler servletContextHandler) {
this.server = server;
this.handler = handler;
this.servletContextHandler = servletContextHandler;
}


@Override
public void addWebSocket(@NotNull String mapping, @NotNull Function<Configurable, WebSocketCreator> configurator) {
handler.insertHandler(WebSocketUpgradeHandler.from(
server,
handler,
container -> container.addMapping(mapping, configurator.apply(container))
));
mappings.add(mapping);
public void addWebSocket(@NotNull ServerEndpointConfig endpointConfig) {
// Add jakarta.websocket support
JakartaWebSocketServletContainerInitializer.configure(servletContextHandler, (context, container) -> {
container.addEndpoint(endpointConfig);
this.mappings.add(endpointConfig.getPath());
});
}

@NotNull
Expand Down
Loading

0 comments on commit 0b81f9f

Please sign in to comment.