Skip to content

Commit 4425a30

Browse files
Merge branch 'devel' into dbeaver/pro#7693-jsonb-string-value-fix
2 parents 15f4c51 + e9b1079 commit 4425a30

File tree

86 files changed

+5098
-286
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+5098
-286
lines changed

server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,15 @@
6060
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
6161
import org.jkiss.dbeaver.runtime.DBWorkbench;
6262
import org.jkiss.utils.CommonUtils;
63+
import org.jkiss.utils.function.ThrowableConsumer;
64+
import org.jkiss.utils.function.ThrowableFunction;
6365

6466
import java.lang.reflect.InvocationTargetException;
6567
import java.nio.file.Path;
6668
import java.time.Instant;
6769
import java.util.*;
6870
import java.util.concurrent.CompletableFuture;
6971
import java.util.concurrent.atomic.AtomicInteger;
70-
import java.util.function.Function;
7172
import java.util.stream.Collectors;
7273
/**
7374
* Web session.
@@ -96,7 +97,7 @@ public class WebSession extends BaseWebSession
9697
private final List<WebServerMessage> sessionMessages = new ArrayList<>();
9798

9899
private final Map<String, WebAsyncTaskInfo> asyncTasks = new HashMap<>();
99-
private final Map<String, Function<Object, Object>> attributeDisposers = new HashMap<>();
100+
private final Map<String, ThrowableConsumer<Object, Exception>> attributeDisposers = new HashMap<>();
100101

101102
// Map of auth tokens. Key is authentication provider
102103
private final List<WebAuthInfo> authTokens = new ArrayList<>();
@@ -363,9 +364,14 @@ protected WebSessionProjectImpl createGlobalProject(RMProject project) {
363364
private void resetSessionCache() throws DBCException {
364365
// Clear attributes
365366
synchronized (attributes) {
366-
for (Map.Entry<String, Function<Object, Object>> attrDisposer : attributeDisposers.entrySet()) {
367+
for (Map.Entry<String, ThrowableConsumer<Object, Exception>> attrDisposer : attributeDisposers.entrySet()) {
367368
Object attrValue = attributes.get(attrDisposer.getKey());
368-
attrDisposer.getValue().apply(attrValue);
369+
370+
try {
371+
attrDisposer.getValue().accept(attrValue);
372+
} catch (Exception e) {
373+
log.error("Error disposing attribute '" + attrDisposer.getKey() + "'", e);
374+
}
369375
}
370376
attributeDisposers.clear();
371377
// Remove all non-persistent attributes
@@ -696,7 +702,11 @@ public void setAttribute(String name, Object value, boolean persistent) {
696702
}
697703
}
698704

699-
public <T> T getAttribute(String name, Function<T, T> creator, Function<T, T> disposer) {
705+
public <T, E extends Exception> T getAttribute(
706+
String name,
707+
ThrowableFunction<String, T, E> creator,
708+
ThrowableConsumer<T, E> disposer
709+
) throws E {
700710
synchronized (attributes) {
701711
Object value = attributes.get(name);
702712
if (value instanceof PersistentAttribute persistentAttribute) {
@@ -707,7 +717,7 @@ public <T> T getAttribute(String name, Function<T, T> creator, Function<T, T> di
707717
if (value != null) {
708718
attributes.put(name, value);
709719
if (disposer != null) {
710-
attributeDisposers.put(name, (Function<Object, Object>) disposer);
720+
attributeDisposers.put(name, (ThrowableConsumer<Object, Exception>) disposer);
711721
}
712722
}
713723
}

server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsLongPolling.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
package io.cloudbeaver.server.websockets;
1818

1919
import io.cloudbeaver.model.session.BaseWebSession;
20+
import io.cloudbeaver.model.session.WebHeadlessSession;
2021
import io.cloudbeaver.websocket.CBWebSessionEventHandler;
22+
import io.cloudbeaver.websocket.event.client.WSSessionPingClientEvent;
2123
import org.jkiss.code.NotNull;
2224
import org.jkiss.code.Nullable;
2325
import org.jkiss.dbeaver.Log;
26+
import org.jkiss.dbeaver.model.websocket.WSUtils;
2427
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
2528

2629
import java.util.ArrayList;
@@ -33,6 +36,8 @@ public class CBEventsLongPolling implements CBWebSessionEventHandler {
3336

3437
private static final Log log = Log.getLog(CBEventsLongPolling.class);
3538

39+
private static final String PING = WSUtils.clientGson.toJson(new WSSessionPingClientEvent("cb_session"));
40+
3641
private static final int QUEUE_CAPACITY = 1000;
3742

3843
private final BaseWebSession webSession;
@@ -56,7 +61,16 @@ public long lastPoll() {
5661
return lastPoll;
5762
}
5863

59-
public void touch() {
64+
public void onPoll() {
65+
lastPoll = System.currentTimeMillis();
66+
67+
if (webSession instanceof WebHeadlessSession) {
68+
webSession.touchSession();
69+
processor.process(PING);
70+
}
71+
}
72+
73+
public void onUserActivity() {
6074
lastPoll = System.currentTimeMillis();
6175
webSession.touchSession();
6276
}

server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsLongPollingServlet.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import io.cloudbeaver.server.WebAppSessionManager;
2323
import io.cloudbeaver.server.WebAppUtils;
2424
import io.cloudbeaver.utils.ServletAppUtils;
25-
import io.cloudbeaver.websocket.event.client.WSSessionPingClientEvent;
2625
import jakarta.servlet.ServletException;
2726
import jakarta.servlet.http.HttpServlet;
2827
import jakarta.servlet.http.HttpServletRequest;
@@ -51,7 +50,6 @@ public class CBEventsLongPollingServlet extends HttpServlet {
5150

5251
private static final Log log = Log.getLog(CBEventsLongPollingServlet.class);
5352

54-
private static final String PING = WSUtils.clientGson.toJson(new WSSessionPingClientEvent("cb_session"));
5553
private static final int POLL_TIMEOUT_SEC = 25;
5654
private static final int SESSION_IDLE_TIMEOUT_SEC = 60;
5755

@@ -86,8 +84,7 @@ protected void doGet(@NotNull HttpServletRequest req, @NotNull HttpServletRespon
8684
}
8785

8886
CBEventsLongPolling ps = getOrCreatePollSession(ws);
89-
ps.onMessage(PING);
90-
87+
ps.onPoll();
9188

9289
List<WSEvent> events = ps.pollEvents(POLL_TIMEOUT_SEC);
9390

@@ -115,6 +112,7 @@ protected void doPost(@NotNull HttpServletRequest req, @NotNull HttpServletRespo
115112
}
116113

117114
CBEventsLongPolling ps = getOrCreatePollSession(ws);
115+
ps.onUserActivity();
118116

119117
String json = new String(req.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
120118
if (CommonUtils.isEmpty(json)) {
@@ -222,7 +220,6 @@ private CBEventsLongPolling getOrCreatePollSession(@NotNull BaseWebSession ws) {
222220

223221
return sessions.compute(sid, (key, existing) -> {
224222
if (existing != null) {
225-
existing.touch();
226223
return existing;
227224
}
228225

server/bundles/io.cloudbeaver.service.ldap.auth/src/io/cloudbeaver/service/ldap/auth/LdapAuthProvider.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBruteForceProtected, SMAuthProviderAssigner {
5858
private static final Log log = Log.getLog(LdapAuthProvider.class);
5959
public static final String LDAP_AUTH_PROVIDER_ID = "ldap";
60+
private static final int DEFAULT_TIME_LIMIT = 30_000;
6061

6162
public LdapAuthProvider() {
6263
}
@@ -84,7 +85,7 @@ public Map<String, Object> authExternalUser(
8485
Map<String, String> environment = creteAuthEnvironment(ldapSettings);
8586

8687
Map<String, Object> userData = new HashMap<>();
87-
if (!isFullDN(userName) && CommonUtils.isNotEmpty(ldapSettings.getLoginAttribute())) {
88+
if (!LdapUtils.isFullDN(userName, ldapSettings.getBaseDN()) && CommonUtils.isNotEmpty(ldapSettings.getLoginAttribute())) {
8889
userData = validateAndLoginUserAccessByUsername(userName, password, ldapSettings);
8990
}
9091
if (CommonUtils.isEmpty(userData)) {
@@ -102,7 +103,7 @@ public SMAutoAssign detectAutoAssignments(
102103
@NotNull SMAuthProviderCustomConfiguration providerConfig,
103104
@NotNull Map<String, Object> authParameters
104105
) throws DBException {
105-
List<String> autoAssignmentTeamIds = detectAutoAssignmentTeam(providerConfig, authParameters);
106+
List<String> autoAssignmentTeamIds = detectAutoAssignmentTeam(new LdapSettings(providerConfig), authParameters);
106107
SMAutoAssign smAutoAssign = new SMAutoAssign();
107108
autoAssignmentTeamIds.forEach(smAutoAssign::addExternalTeamId);
108109
return smAutoAssign;
@@ -411,10 +412,6 @@ public Object getInputUsername(@NotNull Map<String, Object> cred) {
411412
return cred.get(LdapConstants.CRED_USER_DN);
412413
}
413414

414-
private boolean isFullDN(String userName) {
415-
return userName.contains(",") && userName.contains("=");
416-
}
417-
418415
private String buildFullUserDN(String userName, LdapSettings ldapSettings) {
419416
String fullUserDN = userName;
420417

@@ -431,7 +428,7 @@ private String buildFullUserDN(String userName, LdapSettings ldapSettings) {
431428
private SearchControls createSearchControls() {
432429
SearchControls searchControls = new SearchControls();
433430
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
434-
searchControls.setTimeLimit(30_000);
431+
searchControls.setTimeLimit(DEFAULT_TIME_LIMIT);
435432
searchControls.setReturningAttributes(new String[]{"*", "+"});
436433
return searchControls;
437434
}
@@ -493,15 +490,14 @@ protected void doCustomModifyUserDataAfterAuthentication(LdapSettings ldapSettin
493490

494491
@NotNull
495492
protected List<String> detectAutoAssignmentTeam(
496-
@NotNull SMAuthProviderCustomConfiguration providerConfig,
493+
@NotNull LdapSettings ldapSettings,
497494
@NotNull Map<String, Object> authParameters
498495
) throws DBException {
499496
String userName = JSONUtils.getString(authParameters, LdapConstants.CRED_USERNAME);
500497
if (CommonUtils.isEmpty(userName)) {
501498
throw new DBException("LDAP user name is empty");
502499
}
503500

504-
LdapSettings ldapSettings = new LdapSettings(providerConfig);
505501
String fullDN = JSONUtils.getString(authParameters, LdapConstants.CRED_USER_DN);
506502
String userDN;
507503
if (!CommonUtils.isEmpty(fullDN)) {
@@ -531,7 +527,7 @@ private String getUserDN(LdapSettings ldapSettings, String displayName) {
531527
}
532528

533529
@NotNull
534-
private List<String> getGroupForMember(String fullDN, LdapSettings ldapSettings, Map<String, Object> authParameters) {
530+
protected List<String> getGroupForMember(String fullDN, LdapSettings ldapSettings, Map<String, Object> authParameters) {
535531
DirContext context = null;
536532
Set<String> result = new LinkedHashSet<>();
537533
try {
@@ -573,11 +569,11 @@ private List<String> getGroupForMember(String fullDN, LdapSettings ldapSettings,
573569
return new ArrayList<>(result);
574570
}
575571

576-
private List<String> findGroupsByMemberOfAttribute(String fullDN, DirContext context) throws NamingException {
572+
protected List<String> findGroupsByMemberOfAttribute(String fullDN, DirContext context) throws NamingException {
577573
List<String> result = new ArrayList<>();
578574
SearchControls memberOfSearch = new SearchControls();
579575
memberOfSearch.setSearchScope(SearchControls.OBJECT_SCOPE);
580-
memberOfSearch.setTimeLimit(30_000);
576+
memberOfSearch.setTimeLimit(DEFAULT_TIME_LIMIT);
581577
memberOfSearch.setReturningAttributes(new String[] {"*", "+"});
582578
NamingEnumeration<SearchResult> userRecord = context.search(fullDN, "(objectClass=*)", memberOfSearch);
583579
try {
@@ -602,7 +598,7 @@ private List<String> findGroupsByMemberOfAttribute(String fullDN, DirContext con
602598
return result;
603599
}
604600

605-
private List<String> findGroupsByMemberAttribute(String fullDN, LdapSettings ldapSettings, DirContext context) throws NamingException {
601+
protected List<String> findGroupsByMemberAttribute(String fullDN, LdapSettings ldapSettings, DirContext context) throws NamingException {
606602
List<String> result = new ArrayList<>();
607603
String searchFilter = "(member={0})";
608604
SearchControls searchControls = new SearchControls();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.cloudbeaver.service.ldap.auth;
2+
3+
import org.jkiss.code.NotNull;
4+
import org.jkiss.code.Nullable;
5+
6+
import javax.naming.InvalidNameException;
7+
import javax.naming.ldap.LdapName;
8+
9+
public class LdapUtils {
10+
11+
public static boolean isFullDN(@Nullable String dn, @NotNull String baseDN) {
12+
if (dn == null) {
13+
return false;
14+
}
15+
String dnTrimmed = dn.trim();
16+
if (dnTrimmed.isEmpty()) {
17+
return false;
18+
}
19+
String baseTrimmed = baseDN.trim();
20+
21+
try {
22+
LdapName dnName = new LdapName(dnTrimmed);
23+
LdapName baseName = new LdapName(baseTrimmed);
24+
return dnName.startsWith(baseName);
25+
} catch (InvalidNameException e) {
26+
return false;
27+
}
28+
}
29+
}

server/drivers/clickhouse_com/pom.xml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
<dependency>
1919
<groupId>com.clickhouse</groupId>
2020
<artifactId>clickhouse-jdbc</artifactId>
21-
<version>0.8.5</version>
22-
<classifier>http</classifier>
21+
<version>0.9.5</version>
2322
<exclusions>
2423
<exclusion>
2524
<groupId>org.apache.commons</groupId>
@@ -34,7 +33,7 @@
3433
<dependency>
3534
<groupId>org.apache.httpcomponents.client5</groupId>
3635
<artifactId>httpclient5</artifactId>
37-
<version>5.3.1</version>
36+
<version>5.4.4</version>
3837
<exclusions>
3938
<exclusion>
4039
<groupId>org.slf4j</groupId>

server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/model/session/WebSessionTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import io.cloudbeaver.DBWebException;
2121
import io.cloudbeaver.model.app.ServletAuthApplication;
2222
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
23+
import org.jkiss.utils.function.ThrowableConsumer;
24+
import org.jkiss.utils.function.ThrowableFunction;
2325
import org.junit.Assert;
2426
import org.junit.Before;
2527
import org.junit.Test;
@@ -28,7 +30,6 @@
2830
import java.util.Collections;
2931
import java.util.Locale;
3032
import java.util.concurrent.atomic.AtomicBoolean;
31-
import java.util.function.Function;
3233

3334
public class WebSessionTest extends CloudbeaverMockTest {
3435

@@ -53,10 +54,9 @@ public void localeAndBasicAttributes() throws Exception {
5354

5455
// Non-persistent attribute created via getAttribute with creator/disposer
5556
AtomicBoolean disposed = new AtomicBoolean(false);
56-
Function<String, String> creator = (s) -> "created";
57-
Function<String, String> disposer = (s) -> {
57+
ThrowableFunction<String, String, Exception> creator = (s) -> "created";
58+
ThrowableConsumer<String, Exception> disposer = (s) -> {
5859
disposed.set(true);
59-
return null;
6060
};
6161

6262
String created = session.getAttribute("createdKey", creator, disposer);
@@ -96,4 +96,4 @@ private ServletAuthApplication mockApplication() {
9696
Mockito.when(app.getEventController()).thenReturn(eventController);
9797
return app;
9898
}
99-
}
99+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!lib

0 commit comments

Comments
 (0)