mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
623f4458d9 | ||
|
|
eaeec9f1be | ||
|
|
dd501d0716 | ||
|
|
1d2e1cd823 | ||
|
|
b73dbe684b | ||
|
|
9b3098d68f | ||
|
|
069218cb5e | ||
|
|
29ec2e09b7 | ||
|
|
5b077f3189 | ||
|
|
73b5e8fc59 | ||
|
|
1073335af5 | ||
|
|
d5cdb8fb43 | ||
|
|
752f913db0 | ||
|
|
0d665a6b7b | ||
|
|
84cbf143ae | ||
|
|
f4a62bbca8 | ||
|
|
d93d7748cf | ||
|
|
d2f1f29e80 | ||
|
|
b7add23754 | ||
|
|
07fc1f88b7 | ||
|
|
c79a9a8efa | ||
|
|
e785d98c9b | ||
|
|
45c201c465 | ||
|
|
664864d478 | ||
|
|
3cdcb07646 | ||
|
|
e33eb9fd60 | ||
|
|
8fba6107b2 | ||
|
|
f41effd655 | ||
|
|
9b34a496dc | ||
|
|
93c9cdd23d | ||
|
|
c7e6cf9d95 | ||
|
|
27b0180107 | ||
|
|
f073954a32 | ||
|
|
e5ed5c8682 | ||
|
|
90cd350267 | ||
|
|
1360631ab4 | ||
|
|
dda35760eb | ||
|
|
1086151bcb | ||
|
|
e7c066e592 | ||
|
|
5aae41d1fe | ||
|
|
4ad921a9c8 | ||
|
|
632c93f246 | ||
|
|
4e4b817a38 | ||
|
|
b097b1147a | ||
|
|
d00f08b9d0 | ||
|
|
9d0ecce84f | ||
|
|
7550915e9c | ||
|
|
78c19187de | ||
|
|
fc592908c2 | ||
|
|
ba0cd2d3fa | ||
|
|
872c226522 | ||
|
|
ab94dd837b | ||
|
|
dce23485a7 | ||
|
|
17510f2de1 | ||
|
|
98d171e503 | ||
|
|
736d91fbe3 | ||
|
|
c0b71a431a |
Vendored
+2
-1
@@ -19,6 +19,7 @@
|
||||
"VisualStudioExptTeam.vscodeintellicode",
|
||||
"yzhang.markdown-all-in-one",
|
||||
"GraphQL.vscode-graphql-syntax",
|
||||
"mrmlnc.vscode-json5"
|
||||
"mrmlnc.vscode-json5",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -22,6 +22,20 @@ You can see live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
|
||||
## Changelog
|
||||
|
||||
### CloudBeaver 23.1.3 - 2023-07-24
|
||||
|
||||
- Users can simultaneously edit resources, allowing them to work together;
|
||||
- We have improved the UX in the search bar - users can delete a query or request by clicking on the cross icon;
|
||||
- The search request considers file names and exclude the .sql file extension for now;
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
### CloudBeaver 23.1.2 - 2023-07-10
|
||||
|
||||
- We have improved the SQL Editor functionality by adding support for displaying tables with nested arrays of objects;
|
||||
- The ability to compress files during export allows for faster download speeds, particularly for larger files;
|
||||
- New Settings panel displays the product configuration settings such as Minimum fetch size, Maximum fetch size, and Default fetch size from the Data Editor;
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
### CloudBeaver 23.1.1 - 2023-06-26
|
||||
|
||||
- Connections are consistently displayed now when they are pre-configured into the workspace in the Global Configuration json file.
|
||||
|
||||
@@ -3,6 +3,6 @@ FROM eclipse-temurin:17.0.4.1_1-jre-focal
|
||||
COPY cloudbeaver /opt/cloudbeaver
|
||||
|
||||
EXPOSE 8978
|
||||
|
||||
RUN find /opt/cloudbeaver -type d -exec chmod 775 {} \;
|
||||
WORKDIR /opt/cloudbeaver/
|
||||
ENTRYPOINT ["./run-server.sh"]
|
||||
|
||||
@@ -10,4 +10,4 @@ IF NOT EXIST workspace\.metadata (
|
||||
)
|
||||
)
|
||||
|
||||
java %JAVA_OPTS% --add-modules=ALL-SYSTEM --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/jdk.internal.vm=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/sun.security.ssl=ALL-UNNAMED --add-opens=java.base/sun.security.action=ALL-UNNAMED --add-opens=java.base/sun.security.util=ALL-UNNAMED -jar %launcherJar% -product io.cloudbeaver.product.ce.product -web-config conf/cloudbeaver.conf -nl en -registryMultiLanguage
|
||||
java %JAVA_OPTS% --add-modules=ALL-SYSTEM --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/jdk.internal.vm=ALL-UNNAMED --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/sun.security.ssl=ALL-UNNAMED --add-opens=java.base/sun.security.action=ALL-UNNAMED --add-opens=java.base/sun.security.util=ALL-UNNAMED --add-opens=java.security.jgss/sun.security.jgss=ALL-UNNAMED --add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED -jar %launcherJar% -product io.cloudbeaver.product.ce.product -web-config conf/cloudbeaver.conf -nl en -registryMultiLanguage
|
||||
|
||||
@@ -6,4 +6,4 @@ echo "Starting Cloudbeaver Server"
|
||||
|
||||
[ ! -d "workspace/.metadata" ] && mkdir -p workspace/.metadata && mkdir -p workspace/GlobalConfiguration/.dbeaver && [ ! -f "workspace/GlobalConfiguration/.dbeaver/data-sources.json" ] && cp conf/initial-data-sources.conf workspace/GlobalConfiguration/.dbeaver/data-sources.json
|
||||
|
||||
java ${JAVA_OPTS} --add-modules=ALL-SYSTEM --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/jdk.internal.vm=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/sun.security.ssl=ALL-UNNAMED --add-opens=java.base/sun.security.action=ALL-UNNAMED --add-opens=java.base/sun.security.util=ALL-UNNAMED -jar ${launcherJar} -product io.cloudbeaver.product.ce.product -web-config conf/cloudbeaver.conf -nl en -registryMultiLanguage
|
||||
java ${JAVA_OPTS} --add-modules=ALL-SYSTEM --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/jdk.internal.vm=ALL-UNNAMED --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/sun.security.ssl=ALL-UNNAMED --add-opens=java.base/sun.security.action=ALL-UNNAMED --add-opens=java.base/sun.security.util=ALL-UNNAMED --add-opens=java.security.jgss/sun.security.jgss=ALL-UNNAMED --add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED -jar ${launcherJar} -product io.cloudbeaver.product.ce.product -web-config conf/cloudbeaver.conf -nl en -registryMultiLanguage
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<?pde version="3.8"?>
|
||||
<target name="org.jkiss.tp.cloudbeaver">
|
||||
<locations>
|
||||
<location path="${env_var:DBEAVER_DEVEL_ECLIPSE_PATH}/plugins" type="Directory"/>
|
||||
<location path="../eclipse/Eclipse.app/Contents/Eclipse/plugins" type="Directory"/>
|
||||
<location path="../eclipse/plugins" type="Directory"/>
|
||||
</locations>
|
||||
</target>
|
||||
@@ -1,10 +1,10 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
|
||||
Bundle-Version: 1.0.32.qualifier
|
||||
Bundle-Release-Date: 20230710
|
||||
Bundle-Version: 1.0.34.qualifier
|
||||
Bundle-Release-Date: 20230807
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
@@ -22,6 +22,7 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.auth,
|
||||
io.cloudbeaver.auth.provider,
|
||||
io.cloudbeaver.auth.provider.local,
|
||||
io.cloudbeaver.auth.provisioning,
|
||||
io.cloudbeaver.websocket,
|
||||
io.cloudbeaver.model,
|
||||
io.cloudbeaver.model.app,
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver Web Model
|
||||
@@ -3,5 +3,4 @@ output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
plugin.xml,\
|
||||
schema/,\
|
||||
OSGI-INF/
|
||||
schema/
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.32-SNAPSHOT</version>
|
||||
<version>1.0.34-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
*/
|
||||
package io.cloudbeaver;
|
||||
|
||||
import io.cloudbeaver.model.rm.RMUtils;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMControllerProvider;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.registry.BaseProjectImpl;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.Pair;
|
||||
@@ -31,7 +32,7 @@ import org.jkiss.utils.Pair;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
||||
public class BaseWebProjectImpl extends BaseProjectImpl {
|
||||
public class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerProvider {
|
||||
|
||||
@NotNull
|
||||
private final RMProject project;
|
||||
|
||||
@@ -33,9 +33,6 @@ public class DBWConstants {
|
||||
|
||||
public static final String STATE_ATTR_SIGN_IN_STATE = "state.signin";
|
||||
|
||||
public static final String USER_PROJECTS_FOLDER = "user-projects";
|
||||
public static final String SHARED_PROJECTS_FOLDER = "shared-projects";
|
||||
|
||||
public enum SignInState {
|
||||
GLOBAL,
|
||||
EMBEDDED
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver;
|
||||
|
||||
import org.jkiss.dbeaver.model.DBPPage;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class WebPage extends DBPPage {
|
||||
public WebPage(Map<String, Object> params) {
|
||||
super(JSONUtils.getInteger(params, "offset"), JSONUtils.getInteger(params, "limit"));
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -26,6 +26,7 @@ import org.jkiss.dbeaver.model.DBPObject;
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
|
||||
import org.jkiss.dbeaver.model.auth.SMSession;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -41,7 +42,7 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
|
||||
@NotNull
|
||||
default Map<String, Object> authExternalUser(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull Map<String, Object> providerConfig, // Auth provider configuration (e.g. 3rd party auth server address)
|
||||
@Nullable SMAuthProviderCustomConfiguration providerConfig, // Auth provider configuration (e.g. 3rd party auth server address)
|
||||
@NotNull Map<String, Object> authParameters // Passed auth parameters (e.g. user name or password)
|
||||
) throws DBException {
|
||||
return authParameters;
|
||||
@@ -51,7 +52,7 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
|
||||
@NotNull
|
||||
DBWUserIdentity getUserIdentity(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull Map<String, Object> providerConfig,
|
||||
@Nullable SMAuthProviderCustomConfiguration customConfiguration,
|
||||
@NotNull Map<String, Object> authParameters) throws DBException;
|
||||
|
||||
/**
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package io.cloudbeaver.auth.provisioning;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.dbeaver.model.security.user.SMUserProvisioning;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface SMProvisioner {
|
||||
@NotNull
|
||||
List<SMUserProvisioning> listExternalUsers(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull SMAuthProviderCustomConfiguration customConfiguration,
|
||||
@NotNull SMProvisioningFilter filter
|
||||
) throws DBWebException;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package io.cloudbeaver.auth.provisioning;
|
||||
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SMProvisioningFilter {
|
||||
@Nullable
|
||||
private final Integer offset;
|
||||
@Nullable
|
||||
private final Integer limit;
|
||||
|
||||
public SMProvisioningFilter(Map<String, Object> params) {
|
||||
this.offset = JSONUtils.getInteger(params, "offset");
|
||||
this.limit = JSONUtils.getInteger(params, "limit");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getLimit() {
|
||||
return limit;
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class WebConnectionInfo {
|
||||
|
||||
private static final String SECURED_VALUE = "********";
|
||||
public static final String SECURED_VALUE = "********";
|
||||
private final WebSession session;
|
||||
private final DBPDataSourceContainer dataSourceContainer;
|
||||
private WebServerError connectError;
|
||||
|
||||
+12
-1
@@ -22,6 +22,7 @@ import org.jkiss.dbeaver.model.net.DBWHandlerType;
|
||||
import org.jkiss.dbeaver.model.net.ssh.SSHConstants;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -52,6 +53,7 @@ public class WebNetworkHandlerConfig {
|
||||
return configuration.isEnabled();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public SSHConstants.AuthType getAuthType() {
|
||||
return CommonUtils.valueOf(SSHConstants.AuthType.class, configuration.getStringProperty(SSHConstants.PROP_AUTH_TYPE), SSHConstants.AuthType.PASSWORD);
|
||||
}
|
||||
@@ -64,7 +66,7 @@ public class WebNetworkHandlerConfig {
|
||||
return CommonUtils.isEmpty(configuration.getPassword()) ? null : "";
|
||||
}
|
||||
|
||||
|
||||
@Deprecated // use secure properties
|
||||
public String getKey() {
|
||||
return CommonUtils.isEmpty(configuration.getSecureProperty(SSHConstants.PROP_KEY_VALUE)) ? null : "";
|
||||
}
|
||||
@@ -78,4 +80,13 @@ public class WebNetworkHandlerConfig {
|
||||
return configuration.getProperties();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, String> getSecureProperties() {
|
||||
Map<String, String> secureProperties = new LinkedHashMap<>(configuration.getSecureProperties());
|
||||
for (Map.Entry<String, String> property : secureProperties.entrySet()) {
|
||||
property.setValue(CommonUtils.isEmpty(property.getValue()) ? null : WebConnectionInfo.SECURED_VALUE);
|
||||
}
|
||||
return secureProperties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
@@ -20,6 +20,7 @@ import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerType;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -77,4 +78,16 @@ public class WebNetworkHandlerConfigInput {
|
||||
return JSONUtils.getObjectOrNull(cfg, "properties");
|
||||
}
|
||||
|
||||
public Map<String, String> getSecureProperties() {
|
||||
var secureProperties = JSONUtils.getObjectOrNull(cfg, "secureProperties");
|
||||
if (secureProperties == null) {
|
||||
return null;
|
||||
}
|
||||
var result = new LinkedHashMap<String, String>();
|
||||
for (Map.Entry<String, Object> property : secureProperties.entrySet()) {
|
||||
result.put(property.getKey(), CommonUtils.toString(property.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
|
||||
import java.util.Set;
|
||||
@@ -33,5 +34,6 @@ public interface WebAuthConfiguration {
|
||||
|
||||
Set<SMAuthProviderCustomConfiguration> getAuthCustomConfigurations();
|
||||
|
||||
@Nullable
|
||||
SMAuthProviderCustomConfiguration getAuthProviderConfiguration(String configId);
|
||||
}
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ public abstract class DBNAbstractResourceManagerNode extends DBNNode implements
|
||||
super(parentNode);
|
||||
}
|
||||
|
||||
public void removeChildResourceNode(@NotNull Queue<RMResource> resourcePath) {
|
||||
public void removeChildResourceNode(@NotNull Queue<String> resourcePath) {
|
||||
if (children == null || resourcePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public abstract class DBNAbstractResourceManagerNode extends DBNNode implements
|
||||
return;
|
||||
}
|
||||
var expectedResource = resourcePath.poll();
|
||||
var node = RMNavigatorUtils.findResourceNode(children, expectedResource);
|
||||
var node = RMNavigatorUtils.findResourceNode(children, expectedResource.getName());
|
||||
if (node == null) { // we are in expected parent node
|
||||
DBNResourceManagerResource newResourceNode = new DBNResourceManagerResource(this, expectedResource);
|
||||
children = ArrayUtils.add(DBNResourceManagerResource.class, children, newResourceNode);
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
|
||||
return children;
|
||||
}
|
||||
|
||||
public RMController getResourceController() {
|
||||
protected RMController getResourceController() {
|
||||
return ((DBNResourceManagerRoot) getParentNode()).getResourceController();
|
||||
}
|
||||
|
||||
|
||||
+21
-6
@@ -20,6 +20,7 @@ package io.cloudbeaver.model.rm;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPHiddenObject;
|
||||
import org.jkiss.dbeaver.model.DBPImage;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
@@ -34,6 +35,8 @@ import java.util.*;
|
||||
|
||||
public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject, RMEventListener {
|
||||
|
||||
private static final Log log = Log.getLog(DBNResourceManagerRoot.class);
|
||||
|
||||
private DBNResourceManagerProject[] projects;
|
||||
private RMController resourceController;
|
||||
|
||||
@@ -123,10 +126,10 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
var action = event.getAction();
|
||||
switch (action) {
|
||||
case RESOURCE_DELETE:
|
||||
deleteResourceNode(event.getProject(), event.getResourceTree());
|
||||
deleteResourceNode(event.getProject(), event.getResourcePath());
|
||||
break;
|
||||
case RESOURCE_ADD:
|
||||
addResourceNode(event.getProject(), event.getResourceTree());
|
||||
addResourceNode(event.getProject(), event.getResourcePath());
|
||||
break;
|
||||
case PROJECT_ADD:
|
||||
addProjectNode(event.getProject());
|
||||
@@ -134,9 +137,13 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteResourceNode(RMProject project, List<RMResource> resourcePath) {
|
||||
private void deleteResourceNode(RMProject project, String resourcePath) {
|
||||
if (resourcePath == null) {
|
||||
return;
|
||||
}
|
||||
var projectNode = getProjectNode(project);
|
||||
projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.removeChildResourceNode(new ArrayDeque<>(resourcePath)));
|
||||
var rmResourcePath = Arrays.asList(resourcePath.split("/"));
|
||||
projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.removeChildResourceNode(new ArrayDeque<>(rmResourcePath)));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -146,9 +153,17 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private void addResourceNode(RMProject project, List<RMResource> resourcePath) {
|
||||
private void addResourceNode(RMProject project, String resourcePath) {
|
||||
if (resourcePath == null) {
|
||||
return;
|
||||
}
|
||||
var projectNode = getProjectNode(project);
|
||||
projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.addChildResourceNode(new ArrayDeque<>(resourcePath)));
|
||||
try {
|
||||
var rmResourcePath = Arrays.asList(getResourceController().getResourcePath(project.getId(), resourcePath));
|
||||
projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.addChildResourceNode(new ArrayDeque<>(rmResourcePath)));
|
||||
} catch (DBException e) {
|
||||
log.error("Cannot add new node to resource manager tree", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addProjectNode(RMProject project) {
|
||||
|
||||
+2
-2
@@ -3,11 +3,11 @@ package io.cloudbeaver.model.rm;
|
||||
import org.jkiss.dbeaver.model.rm.RMResource;
|
||||
|
||||
class RMNavigatorUtils {
|
||||
public static DBNResourceManagerResource findResourceNode(DBNResourceManagerResource[] resourceNodes, RMResource expectedResource) {
|
||||
public static DBNResourceManagerResource findResourceNode(DBNResourceManagerResource[] resourceNodes, String expectedResource) {
|
||||
DBNResourceManagerResource node = null;
|
||||
for (DBNResourceManagerResource resourceNode : resourceNodes) {
|
||||
var nodeResource = resourceNode.getResource();
|
||||
if (nodeResource.getName().equals(expectedResource.getName()) && nodeResource.isFolder() == expectedResource.isFolder()) {
|
||||
if (nodeResource.getName().equals(expectedResource)) {
|
||||
node = resourceNode;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.rm;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.model.app.BaseWebApplication;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RMUtils {
|
||||
|
||||
public static Path getRootPath() {
|
||||
return DBWorkbench.getPlatform().getWorkspace().getAbsolutePath();
|
||||
}
|
||||
|
||||
public static Path getUserProjectsPath() {
|
||||
return getRootPath().resolve(DBWConstants.USER_PROJECTS_FOLDER);
|
||||
}
|
||||
|
||||
public static Path getSharedProjectsPath() {
|
||||
return getRootPath().resolve(DBWConstants.SHARED_PROJECTS_FOLDER);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Path getProjectPath(RMProject project) {
|
||||
switch (project.getType()) {
|
||||
case GLOBAL:
|
||||
return getRootPath().resolve(BaseWebApplication.getInstance().getDefaultProjectName());
|
||||
case SHARED:
|
||||
return getSharedProjectsPath().resolve(project.getName());
|
||||
default:
|
||||
return getUserProjectsPath().resolve(project.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Different types of project have different location on the workspace. Method returns path to the project.
|
||||
*
|
||||
* @param projectId project name or other identifier
|
||||
* @return path to the project based on the name of projects and prefixes that it contains
|
||||
*/
|
||||
@NotNull
|
||||
public static Path getProjectPathById(@NotNull String projectId) throws DBException {
|
||||
int divPos = projectId.indexOf("_");
|
||||
if (divPos <= 0) {
|
||||
throw new DBException("Bad project ID");
|
||||
}
|
||||
|
||||
String prefix = projectId.substring(0, divPos);
|
||||
String projectName = projectId.substring(divPos + 1);
|
||||
switch (RMProjectType.getByPrefix(prefix)) {
|
||||
case GLOBAL:
|
||||
String defaultProjectName = BaseWebApplication.getInstance().getDefaultProjectName();
|
||||
if (CommonUtils.isEmpty(defaultProjectName)) {
|
||||
throw new DBException("Global projects are not supported");
|
||||
}
|
||||
return getRootPath().resolve(defaultProjectName);
|
||||
case SHARED:
|
||||
return getSharedProjectsPath().resolve(projectName);
|
||||
default:
|
||||
return getUserProjectsPath().resolve(projectName);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> parseProjectPermissions(Set<String> permissions) {
|
||||
return permissions.stream()
|
||||
.map(RMProjectPermission::fromPermission).filter(Objects::nonNull)
|
||||
.flatMap(permission -> permission.getAllPermissions().stream())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static RMProject createAnonymousProject() {
|
||||
RMProject project = new RMProject("anonymous");
|
||||
project.setId("anonymous");
|
||||
project.setType(RMProjectType.USER);
|
||||
project.setProjectPermissions(RMProjectPermission.DATA_SOURCES_EDIT.getAllPermissions().toArray(new String[0]));
|
||||
return project;
|
||||
}
|
||||
}
|
||||
+24
-15
@@ -19,7 +19,6 @@ package io.cloudbeaver.model.rm.local;
|
||||
import io.cloudbeaver.BaseWebProjectImpl;
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.model.rm.RMUtils;
|
||||
import io.cloudbeaver.model.rm.lock.RMFileLockController;
|
||||
import io.cloudbeaver.service.security.SMUtils;
|
||||
import io.cloudbeaver.service.sql.WebSQLConstants;
|
||||
@@ -74,7 +73,7 @@ public class LocalResourceController implements RMController {
|
||||
public static final String DEFAULT_CHANGE_ID = "0";
|
||||
|
||||
private final DBPWorkspace workspace;
|
||||
private final SMCredentialsProvider credentialsProvider;
|
||||
protected final SMCredentialsProvider credentialsProvider;
|
||||
|
||||
private final Path rootPath;
|
||||
private final Path userProjectsPath;
|
||||
@@ -289,6 +288,7 @@ public class LocalResourceController implements RMController {
|
||||
throw new DBException("Project '" + name + "' not created");
|
||||
}
|
||||
try {
|
||||
log.debug("Creating project '" + project.getId() + "'");
|
||||
Files.createDirectories(projectPath);
|
||||
if (WebAppUtils.getWebApplication().isMultiNode()) {
|
||||
createResourceTypeFolders(projectPath);
|
||||
@@ -309,6 +309,7 @@ public class LocalResourceController implements RMController {
|
||||
throw new DBException("Project '" + project.getName() + "' doesn't exists");
|
||||
}
|
||||
try {
|
||||
log.debug("Deleting project '" + projectId + "'");
|
||||
IOUtils.deleteDirectory(targetPath);
|
||||
getSecurityController().deleteAllObjectPermissions(projectId, SMObjects.PROJECT);
|
||||
} catch (IOException e) {
|
||||
@@ -346,6 +347,7 @@ public class LocalResourceController implements RMController {
|
||||
BaseWebProjectImpl webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
() -> {
|
||||
log.debug("Updating value for property '" + propName + "' in project '" + projectId + "'");
|
||||
webProject.setProjectProperty(propName, propValue);
|
||||
return null;
|
||||
}
|
||||
@@ -355,12 +357,12 @@ public class LocalResourceController implements RMController {
|
||||
@Override
|
||||
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds) throws DBException {
|
||||
DBPProject projectMetadata = getWebProject(projectId, false);
|
||||
DBPDataSourceRegistry registry = projectMetadata.getDataSourceRegistry();
|
||||
registry.checkForErrors();
|
||||
return doFileReadOperation(
|
||||
projectId,
|
||||
projectMetadata.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = projectMetadata.getDataSourceRegistry();
|
||||
registry.checkForErrors();
|
||||
DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer();
|
||||
Predicate<DBPDataSourceContainer> filter = null;
|
||||
if (!ArrayUtils.isEmpty(dataSourceIds)) {
|
||||
@@ -404,6 +406,7 @@ public class LocalResourceController implements RMController {
|
||||
false
|
||||
);
|
||||
registry.checkForErrors();
|
||||
log.debug("Save data sources configuration in project '" + projectId + "'");
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.checkForErrors();
|
||||
return configChanged;
|
||||
@@ -423,6 +426,7 @@ public class LocalResourceController implements RMController {
|
||||
DBPDataSourceContainer dataSource = registry.getDataSource(dataSourceId);
|
||||
|
||||
if (dataSource != null) {
|
||||
log.debug("Deleting data source '" + dataSourceId + "' in project '" + projectId + "'");
|
||||
registry.removeDataSource(dataSource);
|
||||
} else {
|
||||
log.warn("Could not find datasource " + dataSourceId + " for deletion");
|
||||
@@ -439,6 +443,7 @@ public class LocalResourceController implements RMController {
|
||||
@NotNull String folderPath) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
@@ -468,6 +473,7 @@ public class LocalResourceController implements RMController {
|
||||
for (String folderPath : folderPaths) {
|
||||
DBPDataSourceFolder folder = registry.getFolder(folderPath);
|
||||
if (folder != null) {
|
||||
log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
registry.removeFolder(folder, dropContents);
|
||||
} else {
|
||||
log.warn("Can not find folder by path [" + folderPath + "] for deletion");
|
||||
@@ -488,6 +494,7 @@ public class LocalResourceController implements RMController {
|
||||
) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
log.debug("Moving data source folder from '" + oldPath + "' to '" + newPath + "' in project '" + projectId + "'");
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
@@ -566,6 +573,7 @@ public class LocalResourceController implements RMController {
|
||||
if (Files.exists(targetPath)) {
|
||||
throw new DBException("Resource '" + resourcePath + "' already exists");
|
||||
}
|
||||
log.debug("Creating resource '" + resourcePath + "' in project '" + projectId + "'");
|
||||
createFolder(targetPath.getParent());
|
||||
doFileWriteOperation(projectId, targetPath, () -> {
|
||||
try {
|
||||
@@ -595,10 +603,10 @@ public class LocalResourceController implements RMController {
|
||||
var normalizedOldResourcePath = CommonUtils.normalizeResourcePath(oldResourcePath);
|
||||
var normalizedNewResourcePath = CommonUtils.normalizeResourcePath(newResourcePath);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Moving resource from '" + normalizedOldResourcePath + "' to '" + normalizedNewResourcePath + "'");
|
||||
log.debug("Moving resource from '" + normalizedOldResourcePath + "' to '" + normalizedNewResourcePath +
|
||||
"' in project '" + projectId + "'") ;
|
||||
}
|
||||
Path oldTargetPath = getTargetPath(projectId, normalizedOldResourcePath);
|
||||
List<RMResource> rmOldResourcePath = makeResourcePath(projectId, oldTargetPath, false);
|
||||
|
||||
doFileWriteOperation(projectId, oldTargetPath, () -> {
|
||||
if (!Files.exists(oldTargetPath)) {
|
||||
@@ -621,7 +629,7 @@ public class LocalResourceController implements RMController {
|
||||
return null;
|
||||
});
|
||||
|
||||
fireRmResourceDeleteEvent(projectId, rmOldResourcePath);
|
||||
fireRmResourceDeleteEvent(projectId, normalizedOldResourcePath);
|
||||
fireRmResourceAddEvent(projectId, normalizedNewResourcePath);
|
||||
}
|
||||
|
||||
@@ -659,11 +667,10 @@ public class LocalResourceController implements RMController {
|
||||
public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "deleteResource")) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Removing resource from '" + resourcePath + "'" + (recursive ? " recursive" : ""));
|
||||
log.debug("Removing resource from '" + resourcePath + "' in project '" + projectId + "'" + (recursive ? " recursive" : ""));
|
||||
}
|
||||
validateResourcePath(resourcePath);
|
||||
Path targetPath = getTargetPath(projectId, resourcePath);
|
||||
List<RMResource> rmResourcePath = makeResourcePath(projectId, targetPath, recursive);
|
||||
doFileWriteOperation(projectId, targetPath, () -> {
|
||||
if (!Files.exists(targetPath)) {
|
||||
throw new DBException("Resource '" + resourcePath + "' doesn't exists");
|
||||
@@ -696,7 +703,7 @@ public class LocalResourceController implements RMController {
|
||||
});
|
||||
|
||||
log.debug("Fire resource delete event");
|
||||
fireRmResourceDeleteEvent(projectId, rmResourcePath);
|
||||
fireRmResourceDeleteEvent(projectId, resourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -762,6 +769,7 @@ public class LocalResourceController implements RMController {
|
||||
doFileWriteOperation(projectId, targetPath, () -> {
|
||||
createFolder(targetPath.getParent());
|
||||
try {
|
||||
log.debug("Writing data to resource '" + targetPath + " in project " + projectId + "'");
|
||||
Files.write(targetPath, data);
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error writing resource '" + resourcePath + "'", e);
|
||||
@@ -799,6 +807,7 @@ public class LocalResourceController implements RMController {
|
||||
BaseWebProjectImpl webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
() -> {
|
||||
log.debug("Updating resource property '" + propertyName + "' in project '" + projectId + "'");
|
||||
webProject.setResourceProperty(resourcePath, propertyName, propertyValue);
|
||||
return null;
|
||||
}
|
||||
@@ -922,21 +931,21 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException {
|
||||
protected <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.projectOpened(projectId);
|
||||
}
|
||||
return operation.doOperation();
|
||||
}
|
||||
|
||||
private <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
protected <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.beforeFileRead(projectId, file);
|
||||
}
|
||||
return operation.doOperation();
|
||||
}
|
||||
|
||||
private <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
protected <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.beforeFileChange(projectId, file);
|
||||
}
|
||||
@@ -1056,11 +1065,11 @@ public class LocalResourceController implements RMController {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_ADD,
|
||||
getProject(projectId, false, false),
|
||||
Arrays.asList(getResourcePath(projectId, resourcePath)))
|
||||
resourcePath)
|
||||
);
|
||||
}
|
||||
|
||||
private void fireRmResourceDeleteEvent(@NotNull String projectId, @NotNull List<RMResource> resourcePath) throws DBException {
|
||||
private void fireRmResourceDeleteEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_DELETE,
|
||||
makeProjectFromId(projectId, false),
|
||||
|
||||
+2
-1
@@ -27,7 +27,6 @@ import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.WebServerMessage;
|
||||
import io.cloudbeaver.model.app.WebAuthApplication;
|
||||
import io.cloudbeaver.model.rm.RMUtils;
|
||||
import io.cloudbeaver.model.user.WebUser;
|
||||
import io.cloudbeaver.service.DBWSessionHandler;
|
||||
import io.cloudbeaver.service.sql.WebSQLConstants;
|
||||
@@ -56,6 +55,7 @@ import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.model.runtime.AbstractJob;
|
||||
import org.jkiss.dbeaver.model.runtime.BaseProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
@@ -467,6 +467,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
SMAuthInfo authInfo = getSecurityController().authenticateAnonymousUser(this.id, getSessionParameters(), CB_SESSION_TYPE);
|
||||
updateSMSession(authInfo);
|
||||
notifySessionAuthChange();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+4
-2
@@ -103,7 +103,6 @@ public class WebSessionAuthProcessor {
|
||||
String userId = curUser.getUserId();
|
||||
|
||||
var securityController = webSession.getSecurityController();
|
||||
Map<String, Object> providerConfig = Collections.emptyMap();
|
||||
var newAuthInfos = new ArrayList<WebAuthInfo>();
|
||||
for (Map.Entry<SMAuthConfigurationReference, Object> entry : authInfo.getAuthData().entrySet()) {
|
||||
SMAuthConfigurationReference authConfiguration = entry.getKey();
|
||||
@@ -138,7 +137,10 @@ public class WebSessionAuthProcessor {
|
||||
}
|
||||
|
||||
DBWUserIdentity userIdentity = null;
|
||||
|
||||
var providerConfigId = authConfiguration.getAuthProviderConfigurationId();
|
||||
var providerConfig = WebAppUtils.getWebAuthApplication()
|
||||
.getAuthConfiguration()
|
||||
.getAuthProviderConfiguration(providerConfigId);
|
||||
if (authProviderExternal != null) {
|
||||
try {
|
||||
userIdentity =
|
||||
|
||||
+2
-2
@@ -17,7 +17,6 @@
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
@@ -25,6 +24,7 @@ import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -92,7 +92,7 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBPProject getProject(@NotNull String projectName) {
|
||||
public WebProjectImpl getProject(@NotNull String projectName) {
|
||||
for (WebProjectImpl project : accessibleProjects) {
|
||||
if (project.getName().equals(projectName)) {
|
||||
return project;
|
||||
|
||||
+17
-7
@@ -80,16 +80,21 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
* refresh context state based on new token from security manager
|
||||
*
|
||||
* @param smAuthInfo - auth info from security manager
|
||||
* @return - true if context changed
|
||||
* @throws DBException - if user already authorized and new token come from another user
|
||||
*/
|
||||
public synchronized boolean refresh(SMAuthInfo smAuthInfo) throws DBException {
|
||||
if (smAuthInfo.getAuthStatus() != SMAuthStatus.SUCCESS || smAuthInfo.getSmAccessToken() == null) {
|
||||
throw new DBCException("Authorization did not complete successfully");
|
||||
}
|
||||
if (smAuthInfo.getAuthPermissions() == null) {
|
||||
if (smAuthInfo.getAuthPermissions() == null && !isAuthorizedInSecurityManager()) {
|
||||
throw new DBCException("Required information about session permissions is missing");
|
||||
}
|
||||
return refresh(smAuthInfo.getSmAccessToken(), smAuthInfo.getSmRefreshToken(), smAuthInfo.getAuthPermissions());
|
||||
boolean sessionChanged = !CommonUtils.equalObjects(smSessionId, smAuthInfo.getAuthPermissions().getSessionId());
|
||||
if (smAuthInfo.getAuthStatus() != SMAuthStatus.SUCCESS || (sessionChanged && smAuthInfo.getSmAccessToken() == null)) {
|
||||
throw new DBCException("Authorization did not complete successfully");
|
||||
}
|
||||
if (sessionChanged) {
|
||||
return refresh(smAuthInfo.getSmAccessToken(), smAuthInfo.getSmRefreshToken(), smAuthInfo.getAuthPermissions());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized boolean refresh(
|
||||
@@ -108,7 +113,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
smAuthPermissions.getSessionId(),
|
||||
smAuthPermissions.getPermissions()
|
||||
);
|
||||
this.refreshToken = smRefreshToken;
|
||||
setRefreshToken(smRefreshToken);
|
||||
setUserPermissions(smAuthPermissions.getPermissions());
|
||||
this.securityController = application.createSecurityController(this);
|
||||
this.adminSecurityController = application.getAdminSecurityController(this);
|
||||
@@ -131,7 +136,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
return;
|
||||
}
|
||||
var newTokens = securityController.refreshSession(refreshToken);
|
||||
this.refreshToken = newTokens.getSmRefreshToken();
|
||||
setRefreshToken(newTokens.getSmRefreshToken());
|
||||
this.smCredentials = new SMCredentials(
|
||||
newTokens.getSmAccessToken(),
|
||||
smCredentials.getUserId(),
|
||||
@@ -204,6 +209,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
*/
|
||||
public synchronized void refreshPermissions() throws DBException {
|
||||
if (isAuthorizedInSecurityManager()) {
|
||||
log.debug("refresh permissions " + getUserId() + " " + getSmSessionId());
|
||||
setUserPermissions(securityController.getTokenPermissions().getPermissions());
|
||||
} else {
|
||||
setUserPermissions(getDefaultPermissions());
|
||||
@@ -238,4 +244,8 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
public Set<String> getAccessibleProjectIds() {
|
||||
return accessibleProjectIds;
|
||||
}
|
||||
|
||||
private void setRefreshToken(@Nullable String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,14 @@ public class WebAppUtils {
|
||||
return (WebApplication) DBWorkbench.getPlatform().getApplication();
|
||||
}
|
||||
|
||||
public static WebAuthApplication getWebAuthApplication() throws DBException {
|
||||
WebApplication application = getWebApplication();
|
||||
if (!WebAuthApplication.class.isAssignableFrom(application.getClass())) {
|
||||
throw new DBException("The current application doesn't contain authorization configuration");
|
||||
}
|
||||
return (WebAuthApplication) application;
|
||||
}
|
||||
|
||||
public static SMAuthenticationManager getAuthManager(WebApplication application) throws DBException {
|
||||
var smController = application.createSecurityController(new NoAuthCredentialsProvider());
|
||||
if (!SMAuthenticationManager.class.isAssignableFrom(smController.getClass())) {
|
||||
@@ -144,12 +152,7 @@ public class WebAppUtils {
|
||||
|
||||
@NotNull
|
||||
public static StringBuilder getAuthApiPrefix(String serviceId) throws DBException {
|
||||
WebApplication application = getWebApplication();
|
||||
if (!WebAuthApplication.class.isAssignableFrom(application.getClass())) {
|
||||
throw new DBException("The current application doesn't contain authorization configuration");
|
||||
}
|
||||
WebAuthApplication webAuthApplication = (WebAuthApplication) application;
|
||||
return getAuthApiPrefix(webAuthApplication, serviceId);
|
||||
return getAuthApiPrefix(getWebAuthApplication(), serviceId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+16
-2
@@ -88,11 +88,25 @@ public class WebDataSourceUtils {
|
||||
if (cfgInput.getPassword() != null) {
|
||||
handlerConfig.setPassword(cfgInput.getPassword());
|
||||
}
|
||||
if (cfgInput.getKey() != null) {
|
||||
setSecureProperties(handlerConfig, cfgInput, true);
|
||||
if (cfgInput.getKey() != null) { // backward compatibility
|
||||
handlerConfig.setSecureProperty(SSHConstants.PROP_KEY_VALUE, cfgInput.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
private static void setSecureProperties(DBWHandlerConfiguration handlerConfig, WebNetworkHandlerConfigInput cfgInput, boolean ignoreNulls) {
|
||||
var secureProperties = cfgInput.getSecureProperties();
|
||||
if (secureProperties == null) {
|
||||
return;
|
||||
}
|
||||
for (var pr : secureProperties.entrySet()) {
|
||||
if (ignoreNulls && pr.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
handlerConfig.setSecureProperty(pr.getKey(), pr.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static DBPDataSourceContainer getLocalOrGlobalDataSource(
|
||||
WebApplication application, WebSession webSession, @Nullable String projectId, String connectionId
|
||||
@@ -121,7 +135,7 @@ public class WebDataSourceUtils {
|
||||
public static void updateHandlerCredentials(DBWHandlerConfiguration handlerCfg, WebNetworkHandlerConfigInput webConfig) {
|
||||
handlerCfg.setUserName(webConfig.getUserName());
|
||||
handlerCfg.setPassword(webConfig.getPassword());
|
||||
handlerCfg.setSecureProperty(SSHConstants.PROP_KEY_VALUE, webConfig.getKey());
|
||||
setSecureProperties(handlerCfg, webConfig, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -130,18 +130,16 @@ public class WebEventUtils {
|
||||
String projectId,
|
||||
WebSession session,
|
||||
String resourcePath,
|
||||
RMResource[] resourceParsedPath,
|
||||
WSConstants.EventAction eventAction,
|
||||
WSResourceProperty property
|
||||
) {
|
||||
addRmResourceUpdatedEvent(projectId, session, resourcePath, resourceParsedPath, eventAction, property, null);
|
||||
addRmResourceUpdatedEvent(projectId, session, resourcePath, eventAction, property, null);
|
||||
}
|
||||
|
||||
public static void addRmResourceUpdatedEvent(
|
||||
String projectId,
|
||||
WebSession session,
|
||||
String resourcePath,
|
||||
RMResource[] resourceParsedPath,
|
||||
WSConstants.EventAction eventAction,
|
||||
WSResourceProperty property,
|
||||
String details
|
||||
@@ -154,7 +152,6 @@ public class WebEventUtils {
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
resourceParsedPath,
|
||||
property,
|
||||
details
|
||||
);
|
||||
@@ -165,7 +162,6 @@ public class WebEventUtils {
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
resourceParsedPath,
|
||||
property,
|
||||
details
|
||||
);
|
||||
@@ -176,7 +172,6 @@ public class WebEventUtils {
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
resourceParsedPath,
|
||||
property,
|
||||
details
|
||||
);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Community Product
|
||||
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
|
||||
Bundle-Version: 23.1.2.qualifier
|
||||
Bundle-Release-Date: 20230710
|
||||
Bundle-Version: 23.1.4.qualifier
|
||||
Bundle-Release-Date: 20230807
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
Require-Bundle: io.cloudbeaver.server
|
||||
Bundle-Localization: OSGI-INF/l10n/bundle
|
||||
Automatic-Module-Name: io.cloudbeaver.product.ce
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver CE bundle
|
||||
@@ -2,5 +2,4 @@ source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
OSGI-INF/,\
|
||||
plugin.xml
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>23.1.2-SNAPSHOT</version>
|
||||
<version>23.1.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Base JDBC drivers
|
||||
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
|
||||
Bundle-Version: 1.0.77.qualifier
|
||||
Bundle-Release-Date: 20230710
|
||||
Bundle-Version: 1.0.79.qualifier
|
||||
Bundle-Release-Date: 20230807
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.77-SNAPSHOT</version>
|
||||
<version>1.0.79-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
|
||||
Bundle-Version: 23.1.2.qualifier
|
||||
Bundle-Release-Date: 20230710
|
||||
Bundle-Version: 23.1.4.qualifier
|
||||
Bundle-Release-Date: 20230807
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
|
||||
@@ -34,5 +34,4 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.service.session,
|
||||
io.cloudbeaver.service.sql
|
||||
Import-Package: org.slf4j
|
||||
Bundle-Localization: OSGI-INF/l10n/bundle
|
||||
Automatic-Module-Name: io.cloudbeaver.server
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver Web Server
|
||||
|
||||
@@ -2,7 +2,6 @@ source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
OSGI-INF/,\
|
||||
schema/,\
|
||||
static/,\
|
||||
plugin.xml
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server</artifactId>
|
||||
<version>23.1.2-SNAPSHOT</version>
|
||||
<version>23.1.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,6 +3,11 @@ scalar Object
|
||||
# Date/Time
|
||||
scalar DateTime
|
||||
|
||||
input PageInput {
|
||||
limit: Int
|
||||
offset: Int
|
||||
}
|
||||
|
||||
type Query
|
||||
|
||||
type Mutation
|
||||
|
||||
@@ -282,15 +282,17 @@ type NetworkHandlerDescriptor {
|
||||
properties: [ObjectPropertyInfo!]!
|
||||
}
|
||||
|
||||
# SSH network handler config. Name without prefix only for backward compatibility
|
||||
type NetworkHandlerConfig {
|
||||
id: ID!
|
||||
enabled: Boolean!
|
||||
authType: NetworkHandlerAuthType!
|
||||
authType: NetworkHandlerAuthType! @deprecated # use properties
|
||||
userName: String
|
||||
password: String
|
||||
key: String
|
||||
key: String @deprecated # use secured properties
|
||||
savePassword: Boolean!
|
||||
properties: Object!
|
||||
secureProperties: Object!
|
||||
}
|
||||
|
||||
# Connection instance
|
||||
@@ -426,12 +428,13 @@ input NavigatorSettingsInput {
|
||||
input NetworkHandlerConfigInput {
|
||||
id: ID!
|
||||
enabled: Boolean
|
||||
authType: NetworkHandlerAuthType
|
||||
authType: NetworkHandlerAuthType @deprecated # use properties
|
||||
userName: String
|
||||
password: String
|
||||
key: String
|
||||
key: String @deprecated # use secured properties
|
||||
savePassword: Boolean
|
||||
properties: Object
|
||||
secureProperties: Object
|
||||
}
|
||||
|
||||
# Configuration of particular connection. Used for new connection create. Includes auth info
|
||||
|
||||
@@ -67,7 +67,7 @@ type NavigatorNodeInfo {
|
||||
# Node type
|
||||
nodeType: String
|
||||
# Can this property have child nodes?
|
||||
hasChildren: Boolean
|
||||
hasChildren: Boolean!
|
||||
# Project id of the node
|
||||
projectId: String
|
||||
|
||||
@@ -82,9 +82,18 @@ type NavigatorNodeInfo {
|
||||
# If is different than properties. It doesn't perform any expensive operation and doesn't require authentication.
|
||||
nodeDetails: [ ObjectPropertyInfo! ]
|
||||
|
||||
folder: Boolean
|
||||
inline: Boolean
|
||||
navigable: Boolean
|
||||
folder: Boolean!
|
||||
inline: Boolean!
|
||||
navigable: Boolean!
|
||||
filtered: Boolean!
|
||||
|
||||
# Reads node filter. Expensive invocation, read only when it is really needed
|
||||
filter: NavigatorNodeFilter
|
||||
}
|
||||
|
||||
type NavigatorNodeFilter {
|
||||
include: [String!]
|
||||
exclude: [String!]
|
||||
}
|
||||
|
||||
type DatabaseCatalog {
|
||||
@@ -134,6 +143,10 @@ extend type Mutation {
|
||||
navDeleteNodes( nodePaths: [ID!]! ): Int
|
||||
|
||||
# Moves nodes with specified IDs to the connection folder
|
||||
navMoveNodesToFolder( nodePaths: [ID!]!, folderPath: ID!): Boolean
|
||||
navMoveNodesToFolder( nodePaths: [ID!]!, folderPath: ID!): Boolean!
|
||||
|
||||
# Sets filter for the folder node. If both include and exclude are null then filter is removed.
|
||||
# Node must be refreshed after applying filters. Node children can be changed
|
||||
navSetFolderFilter( nodePath: ID!, include: [String!], exclude: [String!]): Boolean!
|
||||
|
||||
}
|
||||
+1
@@ -213,6 +213,7 @@ public class WebDatabaseDriverInfo {
|
||||
return new String[0];
|
||||
}
|
||||
return NetworkHandlerRegistry.getInstance().getDescriptors(driver).stream()
|
||||
.filter(h -> !h.isDesktopHandler())
|
||||
.map(NetworkHandlerDescriptor::getId).toArray(String[]::new);
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -17,7 +17,7 @@
|
||||
package io.cloudbeaver.model.user;
|
||||
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.auth.provisioning.SMProvisioner;
|
||||
import io.cloudbeaver.registry.WebAuthProviderConfiguration;
|
||||
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
|
||||
import io.cloudbeaver.server.CBApplication;
|
||||
@@ -78,6 +78,10 @@ public class WebAuthProviderInfo {
|
||||
return descriptor.isPrivate();
|
||||
}
|
||||
|
||||
public boolean isSupportProvisioning() {
|
||||
return descriptor.getInstance() instanceof SMProvisioner;
|
||||
}
|
||||
|
||||
public List<WebAuthProviderConfiguration> getConfigurations() {
|
||||
List<WebAuthProviderConfiguration> result = new ArrayList<>();
|
||||
for (SMAuthProviderCustomConfiguration cfg : CBApplication.getInstance().getAppConfiguration().getAuthCustomConfigurations()) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import io.cloudbeaver.service.DBWServiceServerConfigurator;
|
||||
import io.cloudbeaver.service.security.SMControllerConfiguration;
|
||||
import io.cloudbeaver.service.session.WebSessionManager;
|
||||
import io.cloudbeaver.utils.WebAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.osgi.service.datalocation.Location;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -50,6 +51,7 @@ import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.dbeaver.model.security.SMConstants;
|
||||
import org.jkiss.dbeaver.model.security.SMObjects;
|
||||
import org.jkiss.dbeaver.model.security.user.SMObjectPermissions;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSServerConfigurationChangedEvent;
|
||||
import org.jkiss.dbeaver.registry.BaseApplicationImpl;
|
||||
@@ -262,6 +264,7 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
|
||||
final Runtime runtime = Runtime.getRuntime();
|
||||
initializeAdditionalConfiguration();
|
||||
|
||||
Location instanceLoc = Platform.getInstanceLocation();
|
||||
try {
|
||||
@@ -337,8 +340,11 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
if (configurationMode) {
|
||||
// Try to configure automatically
|
||||
performAutoConfiguration(configPath.toFile().getParentFile());
|
||||
} else if (appConfiguration.isGrantConnectionsAccessToAnonymousTeam() && !isMultiNode()) {
|
||||
grantAnonymousAccessToConnections(appConfiguration, "auto-grant");
|
||||
} else if (!isMultiNode()) {
|
||||
if (appConfiguration.isGrantConnectionsAccessToAnonymousTeam()) {
|
||||
grantAnonymousAccessToConnections(appConfiguration, CBConstants.ADMIN_AUTO_GRANT);
|
||||
}
|
||||
grantPermissionsToConnections();
|
||||
}
|
||||
|
||||
if (enableSecurityManager) {
|
||||
@@ -360,6 +366,10 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
return;
|
||||
}
|
||||
|
||||
protected void initializeAdditionalConfiguration() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures server automatically.
|
||||
* Called on startup
|
||||
@@ -665,6 +675,20 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Object> readConnectionsPermissionsConfiguration(Path parentPath) {
|
||||
String permissionsConfigPath = WebAppUtils.getRelativePath(CBConstants.DEFAULT_DATASOURCE_PERMISSIONS_CONFIGURATION, parentPath);
|
||||
File permissionsConfigFile = new File(permissionsConfigPath);
|
||||
if (permissionsConfigFile.exists()) {
|
||||
log.debug("Load permissions configuration from '" + permissionsConfigFile.getAbsolutePath() + "'");
|
||||
try (Reader reader = new InputStreamReader(new FileInputStream(permissionsConfigFile), StandardCharsets.UTF_8)) {
|
||||
return JSONUtils.parseMap(getGson(), reader);
|
||||
} catch (Exception e) {
|
||||
log.error("Error reading permissions configuration", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Map<String, Object> readConfiguration(File configFile) throws DBException {
|
||||
Map<String, Object> configProps = new LinkedHashMap<>();
|
||||
if (configFile.exists()) {
|
||||
@@ -702,7 +726,7 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
public Map<String, Object> readConfigurationFile(File configFile) throws DBException {
|
||||
try (Reader reader = new InputStreamReader(new FileInputStream(configFile), StandardCharsets.UTF_8)) {
|
||||
return JSONUtils.parseMap(getGson(), reader);
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Error parsing server configuration", e);
|
||||
}
|
||||
}
|
||||
@@ -873,6 +897,35 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
}
|
||||
|
||||
private void grantPermissionsToConnections() {
|
||||
try {
|
||||
var globalRegistry = WebDataSourceUtils.getGlobalDataSourceRegistry();
|
||||
var permissionsConfiguration = readConnectionsPermissionsConfiguration(globalRegistry.getProject().getMetadataFolder(false));
|
||||
if (permissionsConfiguration == null) {
|
||||
return;
|
||||
}
|
||||
for (var entry : permissionsConfiguration.entrySet()) {
|
||||
var dataSourceId = entry.getKey();
|
||||
var ds = globalRegistry.getDataSource(dataSourceId);
|
||||
if (ds == null) {
|
||||
log.error("Connection " + dataSourceId + " is not found in project " + globalRegistry.getProject().getName());
|
||||
}
|
||||
List<String> permissions = JSONUtils.getStringList(permissionsConfiguration, dataSourceId);
|
||||
var securityController = getSecurityController();
|
||||
securityController.deleteAllObjectPermissions(dataSourceId, SMObjects.DATASOURCE);
|
||||
securityController.setObjectPermissions(
|
||||
Set.of(dataSourceId),
|
||||
SMObjects.DATASOURCE,
|
||||
new HashSet<>(permissions),
|
||||
Set.of(SMConstants.DATA_SOURCE_ACCESS_PERMISSION),
|
||||
CBConstants.ADMIN_AUTO_GRANT
|
||||
);
|
||||
}
|
||||
} catch (DBException e) {
|
||||
log.error("Error granting permissions to connections", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void saveRuntimeConfig(
|
||||
String newServerName,
|
||||
String newServerURL,
|
||||
|
||||
@@ -60,6 +60,7 @@ public class CBConstants {
|
||||
public static final String DEFAULT_DRIVERS_LOCATION = DEFAULT_DEPLOY_LOCATION + "/drivers";
|
||||
public static final String DEFAULT_WORKSPACE_LOCATION = DEFAULT_DEPLOY_LOCATION + "/workspace";
|
||||
public static final String DEFAULT_PRODUCT_CONFIGURATION = "conf/product.conf";
|
||||
public static final String DEFAULT_DATASOURCE_PERMISSIONS_CONFIGURATION = "data-sources-permissions.json";
|
||||
public static final String DEFAULT_ADMIN_NAME = "cbadmin";
|
||||
public static final String DEFAULT_ADMIN_TEAM = "admin";
|
||||
|
||||
@@ -79,4 +80,5 @@ public class CBConstants {
|
||||
public static final String APPLICATION_JSON = "application/json";
|
||||
|
||||
public static final String QUOTA_PROP_FILE_LIMIT = "dataExportFileSizeLimit";
|
||||
public static final String ADMIN_AUTO_GRANT = "auto-grant";
|
||||
}
|
||||
|
||||
+4
-11
@@ -43,35 +43,28 @@ public class WSRmResourceUpdatedEventHandlerImpl extends WSAbstractProjectEventH
|
||||
@Override
|
||||
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSResourceUpdatedEvent event) {
|
||||
if (activeUserSession instanceof WebSession) {
|
||||
var parsedResourcePath = event.getResourceParsedPath();
|
||||
var resourceParsedPath = parsedResourcePath instanceof RMResource[]
|
||||
? (RMResource[]) parsedResourcePath
|
||||
: gson.fromJson(gson.toJson(parsedResourcePath), RMResource[].class);
|
||||
var webSession = (WebSession) activeUserSession;
|
||||
acceptChangesInNavigatorTree(
|
||||
WSEventType.valueById(event.getId()),
|
||||
resourceParsedPath,
|
||||
event.getResourcePath(),
|
||||
webSession.getProjectById(event.getProjectId())
|
||||
);
|
||||
}
|
||||
activeUserSession.addSessionEvent(event);
|
||||
}
|
||||
|
||||
private void acceptChangesInNavigatorTree(WSEventType eventType,
|
||||
RMResource[] resourceParsedPath,
|
||||
WebProjectImpl project) {
|
||||
List<RMResource> rmResourcePath = Arrays.asList(resourceParsedPath);
|
||||
private void acceptChangesInNavigatorTree(WSEventType eventType, String resourcePath, WebProjectImpl project) {
|
||||
if (eventType == WSEventType.RM_RESOURCE_CREATED) {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_ADD,
|
||||
project.getRmProject(),
|
||||
rmResourcePath)
|
||||
resourcePath)
|
||||
);
|
||||
} else if (eventType == WSEventType.RM_RESOURCE_DELETED) {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_DELETE,
|
||||
project.getRmProject(),
|
||||
rmResourcePath)
|
||||
resourcePath)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
|
||||
public class SessionStateJob extends PeriodicSystemJob {
|
||||
private static final Log log = Log.getLog(SessionStateJob.class);
|
||||
private static final int PERIOD_MS = 60_000; // once per 60 seconds
|
||||
private static final int PERIOD_MS = 30_000; // once per 60 seconds
|
||||
|
||||
public SessionStateJob(@NotNull CBPlatform platform) {
|
||||
super("Session state sender", platform, PERIOD_MS);
|
||||
|
||||
+1
@@ -174,6 +174,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
throw e.getTargetException();
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
log.error("Unexpected error during gql request", ex);
|
||||
if (SMUtils.isTokenExpiredExceptionWasHandled(ex)) {
|
||||
WebSession webSession = findWebSession(env);
|
||||
if (webSession != null) {
|
||||
|
||||
+8
@@ -49,6 +49,13 @@ public interface DBWServiceNavigator extends DBWService {
|
||||
@NotNull WebSession session,
|
||||
@NotNull String nodePath) throws DBWebException;
|
||||
|
||||
@WebAction
|
||||
boolean setNavigatorNodeFilter(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String nodePath,
|
||||
@Nullable List<String> include,
|
||||
@Nullable List<String> exclude) throws DBWebException;
|
||||
|
||||
@WebAction
|
||||
boolean refreshNavigatorNode(
|
||||
@NotNull WebSession session,
|
||||
@@ -77,4 +84,5 @@ public interface DBWServiceNavigator extends DBWService {
|
||||
@NotNull WebSession session,
|
||||
@NotNull List<String> nodePaths,
|
||||
@NotNull String folderPath) throws DBWebException;
|
||||
|
||||
}
|
||||
|
||||
+16
-3
@@ -36,6 +36,7 @@ import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
|
||||
import org.jkiss.dbeaver.model.struct.DBSEntity;
|
||||
import org.jkiss.dbeaver.model.struct.DBSObject;
|
||||
import org.jkiss.dbeaver.model.struct.DBSObjectFilter;
|
||||
import org.jkiss.dbeaver.model.struct.rdb.DBSProcedure;
|
||||
import org.jkiss.dbeaver.registry.DataSourceFolder;
|
||||
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
|
||||
@@ -133,13 +134,16 @@ public class WebNavigatorNodeInfo {
|
||||
public boolean isNavigable() {
|
||||
if (node instanceof DBNDatabaseNode) {
|
||||
DBNDatabaseNode databaseNode = (DBNDatabaseNode) this.node;
|
||||
if (!databaseNode.getMeta().isNavigable()) {
|
||||
return false;
|
||||
}
|
||||
return databaseNode.getMeta().isNavigable();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isFiltered() {
|
||||
return node.isFiltered();
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isHasChildren() {
|
||||
return node.hasChildren(true);
|
||||
@@ -258,6 +262,15 @@ public class WebNavigatorNodeInfo {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Property
|
||||
public DBSObjectFilter getFilter() throws DBWebException {
|
||||
if (!(node instanceof DBNDatabaseFolder)) {
|
||||
throw new DBWebException("Invalid navigator node type: " + node.getClass().getName());
|
||||
}
|
||||
DBSObjectFilter filter = ((DBNDatabaseFolder) node).getNodeFilter(((DBNDatabaseFolder) node).getItemsMeta(), true);
|
||||
return filter == null || filter.isEmpty() || !filter.isEnabled() ? null : filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return node.getNodeItemPath();
|
||||
|
||||
+6
@@ -63,6 +63,12 @@ public class WebServiceBindingNavigator extends WebServiceBindingBase<DBWService
|
||||
|
||||
));
|
||||
model.getMutationType()
|
||||
.dataFetcher("navSetFolderFilter", env -> getService(env).setNavigatorNodeFilter(
|
||||
getWebSession(env),
|
||||
env.getArgument("nodePath"),
|
||||
env.getArgument("include"),
|
||||
env.getArgument("exclude")
|
||||
))
|
||||
.dataFetcher("navRenameNode", env -> getService(env).renameNode(
|
||||
getWebSession(env),
|
||||
env.getArgument("nodePath"),
|
||||
|
||||
+48
-21
@@ -17,7 +17,9 @@
|
||||
package io.cloudbeaver.service.navigator.impl;
|
||||
|
||||
|
||||
import io.cloudbeaver.BaseWebProjectImpl;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.model.WebCommandContext;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
@@ -43,13 +45,12 @@ import org.jkiss.dbeaver.model.edit.DBEObjectRenamer;
|
||||
import org.jkiss.dbeaver.model.exec.DBCExecutionContext;
|
||||
import org.jkiss.dbeaver.model.exec.DBCExecutionContextDefaults;
|
||||
import org.jkiss.dbeaver.model.navigator.*;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
|
||||
import org.jkiss.dbeaver.model.rm.RMResource;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.struct.DBSObject;
|
||||
import org.jkiss.dbeaver.model.struct.DBSObjectContainer;
|
||||
import org.jkiss.dbeaver.model.struct.DBSObjectFilter;
|
||||
import org.jkiss.dbeaver.model.struct.rdb.DBSCatalog;
|
||||
import org.jkiss.dbeaver.model.struct.rdb.DBSSchema;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
@@ -207,6 +208,45 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setNavigatorNodeFilter(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String nodePath,
|
||||
@Nullable List<String> include,
|
||||
@Nullable List<String> exclude) throws DBWebException {
|
||||
try {
|
||||
DBRProgressMonitor monitor = webSession.getProgressMonitor();
|
||||
|
||||
DBNNode node = webSession.getNavigatorModel().getNodeByPath(monitor, nodePath);
|
||||
if (node == null) {
|
||||
throw new DBWebException("Navigator node '" + nodePath + "' not found");
|
||||
}
|
||||
if (!(node instanceof DBNDatabaseFolder)) {
|
||||
throw new DBWebException("Invalid navigator node type: " + node.getClass().getName());
|
||||
}
|
||||
DBSObjectFilter filter = new DBSObjectFilter();
|
||||
if (!CommonUtils.isEmpty(include)) {
|
||||
filter.setInclude(include);
|
||||
}
|
||||
if (!CommonUtils.isEmpty(exclude)) {
|
||||
filter.setExclude(exclude);
|
||||
}
|
||||
filter.setEnabled(true);
|
||||
((DBNDatabaseFolder) node).setNodeFilter(
|
||||
((DBNDatabaseFolder) node).getItemsMeta(), filter, true);
|
||||
if (hasNodeEditPermission(webSession, node, ((WebProjectImpl) node.getOwnerProject()).getRmProject())) {
|
||||
// Save settings
|
||||
((DBNDatabaseFolder) node).getDataSourceContainer().persistConfiguration();
|
||||
}
|
||||
} catch (DBException e) {
|
||||
if (e instanceof DBWebException) {
|
||||
throw (DBWebException)e;
|
||||
}
|
||||
throw new DBWebException("Error changing navigator node '" + nodePath + "' filters", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean refreshNavigatorNode(
|
||||
@NotNull WebSession session,
|
||||
@@ -425,12 +465,9 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
String projectId = rmNode.getResourceProject().getId();
|
||||
// Get paths from nodes
|
||||
String resourcePath = rmNode.getResourceFolder();
|
||||
RMController rmController = session.getRmController();
|
||||
var oldRmResourcePath = rmController.getResourcePath(projectId, resourcePath);
|
||||
node.rename(session.getProgressMonitor(), newName);
|
||||
var newPath = rmNode.getResourceFolder();
|
||||
var newRmResourcePath = rmController.getResourcePath(projectId, newPath);
|
||||
addRmMoveEvent(session, projectId, resourcePath, newPath, oldRmResourcePath, newRmResourcePath);
|
||||
addRmMoveEvent(session, projectId, resourcePath, newPath);
|
||||
return node.getName();
|
||||
}
|
||||
|
||||
@@ -438,22 +475,18 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
@NotNull WebSession session,
|
||||
String projectId,
|
||||
String oldResourcePath,
|
||||
String newResourcePath,
|
||||
RMResource[] oldRmResourcePath,
|
||||
RMResource[] newRmResourcePath
|
||||
String newResourcePath
|
||||
) {
|
||||
WebEventUtils.addRmResourceUpdatedEvent(
|
||||
projectId,
|
||||
session,
|
||||
oldResourcePath,
|
||||
oldRmResourcePath,
|
||||
WSConstants.EventAction.DELETE,
|
||||
WSResourceProperty.NAME);
|
||||
WebEventUtils.addRmResourceUpdatedEvent(
|
||||
projectId,
|
||||
session,
|
||||
newResourcePath,
|
||||
newRmResourcePath,
|
||||
WSConstants.EventAction.CREATE,
|
||||
WSResourceProperty.NAME);
|
||||
}
|
||||
@@ -515,13 +548,11 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
DBNResourceManagerResource rmResource = ((DBNResourceManagerResource) node);
|
||||
String resourceProjectId = rmResource.getResourceProject().getId();
|
||||
String resourcePath = rmResource.getResourceFolder();
|
||||
var rmResourcePath = session.getRmController().getResourcePath(resourceProjectId, resourcePath);
|
||||
session.getRmController().deleteResource(resourceProjectId, resourcePath, true);
|
||||
WebEventUtils.addRmResourceUpdatedEvent(
|
||||
resourceProjectId,
|
||||
session,
|
||||
resourcePath,
|
||||
rmResourcePath,
|
||||
WSConstants.EventAction.DELETE,
|
||||
WSResourceProperty.NAME);
|
||||
}
|
||||
@@ -537,9 +568,8 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
}
|
||||
|
||||
private void checkProjectEditAccess(DBNNode node, WebSession session) throws DBException {
|
||||
var project = session.getProjectById(node.getOwnerProject().getId());
|
||||
if (project == null || !hasNodeEditPermission(session, node, project.getRmProject())
|
||||
) {
|
||||
BaseWebProjectImpl project = (BaseWebProjectImpl) node.getOwnerProject();
|
||||
if (project == null || !hasNodeEditPermission(session, node, project.getRmProject())) {
|
||||
throw new DBException("Access denied");
|
||||
}
|
||||
}
|
||||
@@ -614,11 +644,8 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
newPath = ((DBNResourceManagerResource) folderNode).getResourceFolder() + "/" + newPath;
|
||||
}
|
||||
String resourcePath = rmOldNode.getResourceFolder();
|
||||
RMController rmController = session.getRmController();
|
||||
var oldRmResourcePath = rmController.getResourcePath(projectId, resourcePath);
|
||||
rmController.moveResource(projectId, resourcePath, newPath);
|
||||
var newRmResourcePath = rmController.getResourcePath(projectId, newPath);
|
||||
addRmMoveEvent(session, projectId, resourcePath, newPath, oldRmResourcePath, newRmResourcePath);
|
||||
session.getRmController().moveResource(projectId, resourcePath, newPath);
|
||||
addRmMoveEvent(session, projectId, resourcePath, newPath);
|
||||
} else {
|
||||
throw new DBWebException("Navigator node '" + path + "' is not a data source node");
|
||||
}
|
||||
|
||||
+10
-3
@@ -326,11 +326,18 @@ public class WebSessionManager {
|
||||
/**
|
||||
* Send session state with remaining alive time to all cached session
|
||||
*/
|
||||
public void sendSessionsStates() throws DBException {
|
||||
public void sendSessionsStates() {
|
||||
synchronized (sessionMap) {
|
||||
for (var session : sessionMap.values()) {
|
||||
session.getUserContext().refreshPermissions();
|
||||
session.addSessionEvent(new WSSessionStateEvent(session.getRemainingTime(), session.isValid()));
|
||||
if (session instanceof WebHeadlessSession) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
session.getUserContext().refreshPermissions();
|
||||
session.addSessionEvent(new WSSessionStateEvent(session.getRemainingTime(), session.isValid()));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to refresh session state: " + session.getSessionId(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Administration
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true
|
||||
Bundle-Version: 1.0.76.qualifier
|
||||
Bundle-Release-Date: 20230710
|
||||
Bundle-Version: 1.0.78.qualifier
|
||||
Bundle-Release-Date: 20230807
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
Require-Bundle: io.cloudbeaver.server,
|
||||
org.jkiss.dbeaver.registry
|
||||
Bundle-Localization: OSGI-INF/l10n/bundle
|
||||
Automatic-Module-Name: io.cloudbeaver.service.admin
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver Web Service - Metadata
|
||||
@@ -2,6 +2,5 @@ source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
OSGI-INF/,\
|
||||
schema/,\
|
||||
plugin.xml
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.admin</artifactId>
|
||||
<version>1.0.76-SNAPSHOT</version>
|
||||
<version>1.0.78-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -114,11 +114,16 @@ input ServerConfigInput {
|
||||
sessionExpireTime: Int
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
input AdminUserFilterInput {
|
||||
userIdMask: String
|
||||
enabledState: Boolean
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
#### Users and teams
|
||||
|
||||
listUsers(userId: ID): [AdminUserInfo!]!
|
||||
adminUserInfo(userId: ID!): AdminUserInfo!
|
||||
listUsers(page: PageInput!, filter: AdminUserFilterInput!): [AdminUserInfo!]!
|
||||
listTeams(teamId: ID): [AdminTeamInfo!]!
|
||||
listPermissions: [AdminPermissionInfo!]!
|
||||
listAuthRoles: [String!]!
|
||||
@@ -138,6 +143,8 @@ extend type Query {
|
||||
|
||||
setUserCredentials(userId: ID!, providerId: ID!, credentials: Object!): Boolean
|
||||
|
||||
deleteUserCredentials(userId: ID!, providerId: ID!): Boolean
|
||||
|
||||
enableUser(userId: ID!, enabled: Boolean!): Boolean
|
||||
|
||||
setUserAuthRole(userId: ID!, authRole: String): Boolean
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.admin;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.DBPPage;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.security.user.SMUserFilter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class AdminUserInfoFilter {
|
||||
@NotNull
|
||||
private final SMUserFilter filter;
|
||||
|
||||
public AdminUserInfoFilter(@NotNull Map<String, Object> params, @NotNull Map<String, Object> pageParams) {
|
||||
DBPPage page = new DBPPage(
|
||||
JSONUtils.getInteger(pageParams, "offset", 0),
|
||||
JSONUtils.getInteger(pageParams, "limit", Integer.MAX_VALUE)
|
||||
);
|
||||
|
||||
Boolean enabledState = params.containsKey("enabledState") ? JSONUtils.getBoolean(params, "enabledState") : null;
|
||||
this.filter = new SMUserFilter(
|
||||
JSONUtils.getString(params, "userIdMask"),
|
||||
enabledState,
|
||||
page
|
||||
);
|
||||
}
|
||||
|
||||
public SMUserFilter getFilter() {
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
+9
-2
@@ -31,7 +31,6 @@ import org.jkiss.dbeaver.model.security.SMDataSourceGrant;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Web service API
|
||||
@@ -40,7 +39,12 @@ public interface DBWServiceAdmin extends DBWService {
|
||||
|
||||
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
@NotNull
|
||||
List<AdminUserInfo> listUsers(@NotNull WebSession webSession, @Nullable String userName) throws DBWebException;
|
||||
AdminUserInfo getUserById(@NotNull WebSession webSession, @NotNull String userId) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
@NotNull
|
||||
List<AdminUserInfo> listUsers(@NotNull WebSession webSession, AdminUserInfoFilter filter)
|
||||
throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
@NotNull
|
||||
@@ -91,6 +95,9 @@ public interface DBWServiceAdmin extends DBWService {
|
||||
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
boolean setUserCredentials(@NotNull WebSession webSession, @NotNull String userID, @NotNull String providerId, @NotNull Map<String, Object> credentials) throws DBWebException;
|
||||
|
||||
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
|
||||
boolean deleteUserCredentials(@NotNull WebSession webSession, @NotNull String userId, @NotNull String providerId) throws DBWebException;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Connection management
|
||||
|
||||
|
||||
+15
-3
@@ -31,7 +31,8 @@ import org.jkiss.utils.CommonUtils;
|
||||
/**
|
||||
* Web service implementation
|
||||
*/
|
||||
public class WebServiceBindingAdmin extends WebServiceBindingBase<DBWServiceAdmin> implements DBWServiceBindingServlet<CBApplication> {
|
||||
public class WebServiceBindingAdmin extends WebServiceBindingBase<DBWServiceAdmin>
|
||||
implements DBWServiceBindingServlet<CBApplication> {
|
||||
|
||||
private static final String SCHEMA_FILE_NAME = "schema/service.admin.graphqls";
|
||||
|
||||
@@ -42,8 +43,17 @@ public class WebServiceBindingAdmin extends WebServiceBindingBase<DBWServiceAdmi
|
||||
@Override
|
||||
public void bindWiring(DBWBindingContext model) throws DBWebException {
|
||||
model.getQueryType()
|
||||
.dataFetcher("listUsers",
|
||||
env -> getService(env).listUsers(getWebSession(env), env.getArgument("userId")))
|
||||
.dataFetcher(
|
||||
"adminUserInfo",
|
||||
env -> getService(env).getUserById(getWebSession(env), env.getArgument("userId"))
|
||||
)
|
||||
.dataFetcher(
|
||||
"listUsers",
|
||||
env -> getService(env).listUsers(
|
||||
getWebSession(env),
|
||||
new AdminUserInfoFilter(env.getArgument("filter"), env.getArgument("page"))
|
||||
)
|
||||
)
|
||||
.dataFetcher("listTeams",
|
||||
env -> getService(env).listTeams(getWebSession(env), env.getArgument("teamId")))
|
||||
.dataFetcher("listPermissions",
|
||||
@@ -84,6 +94,8 @@ public class WebServiceBindingAdmin extends WebServiceBindingBase<DBWServiceAdmi
|
||||
env -> getService(env).setSubjectPermissions(getWebSession(env), env.getArgument("subjectId"), env.getArgument("permissions")))
|
||||
.dataFetcher("setUserCredentials",
|
||||
env -> getService(env).setUserCredentials(getWebSession(env), env.getArgument("userId"), env.getArgument("providerId"), env.getArgument("credentials")))
|
||||
.dataFetcher("deleteUserCredentials",
|
||||
env -> getService(env).deleteUserCredentials(getWebSession(env), env.getArgument("userId"), env.getArgument("providerId")))
|
||||
.dataFetcher("enableUser",
|
||||
env -> getService(env).enableUser(getWebSession(env), env.getArgument("userId"), env.getArgument("enabled")))
|
||||
.dataFetcher("setUserAuthRole",
|
||||
|
||||
+29
-6
@@ -22,7 +22,6 @@ import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.app.BaseWebApplication;
|
||||
import io.cloudbeaver.model.session.WebAuthInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.user.WebUser;
|
||||
@@ -66,16 +65,26 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
.flatMap(service -> service.getPermissions().stream())
|
||||
.collect(Collectors.toMap(WebPermissionDescriptor::getId, Function.identity()));
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AdminUserInfo getUserById(@NotNull WebSession webSession, @NotNull String userId) throws DBWebException {
|
||||
try {
|
||||
SMUser smUser = webSession.getAdminSecurityController().getUserById(userId);
|
||||
return new AdminUserInfo(webSession, new WebUser(smUser));
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error getting user - " + userId, e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<AdminUserInfo> listUsers(@NotNull WebSession webSession, String userName) throws DBWebException {
|
||||
public List<AdminUserInfo> listUsers(@NotNull WebSession webSession, @NotNull AdminUserInfoFilter adminFilter) throws DBWebException {
|
||||
try {
|
||||
List<AdminUserInfo> webUsers = new ArrayList<>();
|
||||
for (SMUser smUser : webSession.getAdminSecurityController().findUsers(userName)) {
|
||||
webUsers.add(new AdminUserInfo(webSession, new WebUser(smUser)));
|
||||
List<AdminUserInfo> users = new ArrayList<>();
|
||||
for (SMUser smUser : webSession.getAdminSecurityController().findUsers(adminFilter.getFilter())) {
|
||||
users.add(new AdminUserInfo(webSession, new WebUser(smUser)));
|
||||
}
|
||||
return webUsers;
|
||||
return users;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error reading users", e);
|
||||
}
|
||||
@@ -318,6 +327,20 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteUserCredentials(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String userId,
|
||||
@NotNull String providerId
|
||||
) throws DBWebException {
|
||||
try {
|
||||
webSession.getAdminSecurityController().deleteUserCredentials(userId, providerId);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new DBWebException("Error setting user credentials", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean enableUser(@NotNull WebSession webSession, @NotNull String userID, @NotNull Boolean enabled) throws DBWebException {
|
||||
WebUser grantor = webSession.getUser();
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Authentication
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true
|
||||
Bundle-Version: 1.0.76.qualifier
|
||||
Bundle-Release-Date: 20230710
|
||||
Bundle-Version: 1.0.78.qualifier
|
||||
Bundle-Release-Date: 20230807
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
Require-Bundle: io.cloudbeaver.server
|
||||
Bundle-Localization: OSGI-INF/l10n/bundle
|
||||
Automatic-Module-Name: io.cloudbeaver.service.auth
|
||||
Export-Package: io.cloudbeaver.service.auth
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver Web Service - Metadata
|
||||
@@ -2,6 +2,5 @@ source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
OSGI-INF/,\
|
||||
schema/,\
|
||||
plugin.xml
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.auth</artifactId>
|
||||
<version>1.0.76-SNAPSHOT</version>
|
||||
<version>1.0.78-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -58,6 +58,7 @@ type AuthProviderInfo {
|
||||
defaultProvider: Boolean!
|
||||
trusted: Boolean!
|
||||
private: Boolean!
|
||||
supportProvisioning: Boolean!
|
||||
|
||||
# Configurable providers must be configured first. See configurations field.
|
||||
configurable: Boolean!
|
||||
|
||||
+13
-3
@@ -33,15 +33,17 @@ import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthInfo;
|
||||
import org.jkiss.dbeaver.model.security.SMConstants;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.model.security.SMStandardMeta;
|
||||
import org.jkiss.dbeaver.model.security.exception.SMException;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class RPSessionHandler implements DBWSessionHandler {
|
||||
|
||||
@@ -68,15 +70,23 @@ public class RPSessionHandler implements DBWSessionHandler {
|
||||
SMAuthProviderExternal<?> authProviderExternal = (SMAuthProviderExternal<?>) authProvider.getInstance();
|
||||
String userName = request.getHeader(RPAuthProvider.X_USER);
|
||||
String teams = request.getHeader(RPAuthProvider.X_ROLE);
|
||||
String firstName = request.getHeader(RPAuthProvider.X_FIRST_NAME);
|
||||
String lastName = request.getHeader(RPAuthProvider.X_LAST_NAME);
|
||||
List<String> userTeams = teams == null ? Collections.emptyList() : List.of(teams.split("\\|"));
|
||||
if (userName != null) {
|
||||
try {
|
||||
Map<String, Object> credentials = new HashMap<>();
|
||||
credentials.put("user", userName);
|
||||
if (!CommonUtils.isEmpty(firstName)) {
|
||||
credentials.put(SMStandardMeta.META_FIRST_NAME, firstName);
|
||||
}
|
||||
if (!CommonUtils.isEmpty(lastName)) {
|
||||
credentials.put(SMStandardMeta.META_LAST_NAME, lastName);
|
||||
}
|
||||
Map<String, Object> sessionParameters = webSession.getSessionParameters();
|
||||
sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_TEAMS, userTeams);
|
||||
Map<String, Object> userCredentials = authProviderExternal.authExternalUser(
|
||||
webSession.getProgressMonitor(), sessionParameters, credentials);
|
||||
webSession.getProgressMonitor(), null, credentials);
|
||||
String currentSmSessionId = webSession.getUser() == null ? null : webSession.getUserContext().getSmSessionId();
|
||||
try {
|
||||
SMAuthInfo smAuthInfo = securityController.authenticate(
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Data Transfer
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.data.transfer;singleton:=true
|
||||
Bundle-Version: 1.0.77.qualifier
|
||||
Bundle-Release-Date: 20230710
|
||||
Bundle-Version: 1.0.79.qualifier
|
||||
Bundle-Release-Date: 20230807
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
Require-Bundle: io.cloudbeaver.server,
|
||||
org.jkiss.dbeaver.data.transfer,
|
||||
org.jkiss.dbeaver.registry
|
||||
Bundle-Localization: OSGI-INF/l10n/bundle
|
||||
Automatic-Module-Name: io.cloudbeaver.service.data.transfer
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver Web Service - Data Transfer
|
||||
@@ -2,6 +2,5 @@ source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
OSGI-INF/,\
|
||||
schema/,\
|
||||
plugin.xml
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.data.transfer</artifactId>
|
||||
<version>1.0.77-SNAPSHOT</version>
|
||||
<version>1.0.79-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Metadata
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.metadata;singleton:=true
|
||||
Bundle-Version: 1.0.80.qualifier
|
||||
Bundle-Release-Date: 20230710
|
||||
Bundle-Version: 1.0.82.qualifier
|
||||
Bundle-Release-Date: 20230807
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver Web Service - Metadata
|
||||
@@ -2,6 +2,5 @@ source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
OSGI-INF/,\
|
||||
schema/,\
|
||||
plugin.xml
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.metadata</artifactId>
|
||||
<version>1.0.80-SNAPSHOT</version>
|
||||
<version>1.0.82-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Resource manager
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.rm;singleton:=true
|
||||
Bundle-Version: 1.0.29.qualifier
|
||||
Bundle-Release-Date: 20230710
|
||||
Bundle-Version: 1.0.31.qualifier
|
||||
Bundle-Release-Date: 20230807
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
Require-Bundle: io.cloudbeaver.server,
|
||||
io.cloudbeaver.service.admin
|
||||
Bundle-Localization: OSGI-INF/l10n/bundle
|
||||
Automatic-Module-Name: io.cloudbeaver.service.rm
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver Web Service - Resource manager
|
||||
@@ -2,6 +2,5 @@ source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
OSGI-INF/,\
|
||||
schema/,\
|
||||
plugin.xml
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.rm</artifactId>
|
||||
<version>1.0.29-SNAPSHOT</version>
|
||||
<version>1.0.31-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+1
-10
@@ -118,7 +118,6 @@ public class WebServiceRM implements DBWServiceRM {
|
||||
projectId,
|
||||
webSession,
|
||||
resourcePath,
|
||||
getResourceController(webSession).getResourcePath(projectId, resourcePath),
|
||||
WSConstants.EventAction.UPDATE,
|
||||
WSResourceProperty.PROPERTY,
|
||||
propertyName);
|
||||
@@ -156,7 +155,6 @@ public class WebServiceRM implements DBWServiceRM {
|
||||
projectId,
|
||||
webSession,
|
||||
resourcePath,
|
||||
getResourceController(webSession).getResourcePath(projectId, resourcePath),
|
||||
WSConstants.EventAction.CREATE,
|
||||
WSResourceProperty.NAME);
|
||||
return result;
|
||||
@@ -172,13 +170,11 @@ public class WebServiceRM implements DBWServiceRM {
|
||||
) throws DBException {
|
||||
checkIsRmEnabled(webSession);
|
||||
try {
|
||||
var rmResourcePath = getResourceController(webSession).getResourcePath(projectId, resourcePath);
|
||||
getResourceController(webSession).deleteResource(projectId, resourcePath, false);
|
||||
WebEventUtils.addRmResourceUpdatedEvent(
|
||||
projectId,
|
||||
webSession,
|
||||
resourcePath,
|
||||
rmResourcePath,
|
||||
WSConstants.EventAction.DELETE,
|
||||
WSResourceProperty.NAME);
|
||||
return true;
|
||||
@@ -196,22 +192,18 @@ public class WebServiceRM implements DBWServiceRM {
|
||||
checkIsRmEnabled(webSession);
|
||||
try {
|
||||
var resourceController = getResourceController(webSession);
|
||||
var oldRmResourcePath = resourceController.getResourcePath(projectId, oldResourcePath);
|
||||
resourceController.moveResource(projectId, oldResourcePath, newResourcePath);;
|
||||
var newRmResourcePath = resourceController.getResourcePath(projectId, newResourcePath);
|
||||
resourceController.moveResource(projectId, oldResourcePath, newResourcePath);
|
||||
|
||||
WebEventUtils.addRmResourceUpdatedEvent(
|
||||
projectId,
|
||||
webSession,
|
||||
oldResourcePath,
|
||||
oldRmResourcePath,
|
||||
WSConstants.EventAction.DELETE,
|
||||
WSResourceProperty.NAME);
|
||||
WebEventUtils.addRmResourceUpdatedEvent(
|
||||
projectId,
|
||||
webSession,
|
||||
newResourcePath,
|
||||
newRmResourcePath,
|
||||
WSConstants.EventAction.CREATE,
|
||||
WSResourceProperty.NAME);
|
||||
return true;
|
||||
@@ -241,7 +233,6 @@ public class WebServiceRM implements DBWServiceRM {
|
||||
projectId,
|
||||
webSession,
|
||||
resourcePath,
|
||||
getResourceController(webSession).getResourcePath(projectId, resourcePath),
|
||||
eventType,
|
||||
WSResourceProperty.CONTENT);
|
||||
return content;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Security Sevice
|
||||
Bundle-Vendor: Cloudbeaver Web Service - Security
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.security;singleton:=true
|
||||
Bundle-Version: 1.0.32.qualifier
|
||||
Bundle-Release-Date: 20230710
|
||||
Bundle-Version: 1.0.34.qualifier
|
||||
Bundle-Release-Date: 20230807
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver Web Service - Security
|
||||
@@ -3,5 +3,4 @@ output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
db/,\
|
||||
plugin.xml,\
|
||||
OSGI-INF/
|
||||
plugin.xml
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
</credentials>
|
||||
|
||||
</authProvider>
|
||||
<authProvider id="reverseProxy" label="Reverse proxy" description="Reverse proxy header based authentication" trusted="true" class="io.cloudbeaver.auth.provider.rp.RPAuthProvider">
|
||||
<authProvider id="reverseProxy" label="Reverse proxy"
|
||||
description="Reverse proxy header based authentication"
|
||||
trusted="true"
|
||||
class="io.cloudbeaver.auth.provider.rp.RPAuthProvider"
|
||||
icon="platform:/plugin/org.jkiss.dbeaver.model/icons/tree/key.png">
|
||||
<credentials>
|
||||
<propertyGroup label="General">
|
||||
<property id="user" label="User name" type="string" description="User name" admin="true" user="true" identifying="true"/>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.security</artifactId>
|
||||
<version>1.0.32-SNAPSHOT</version>
|
||||
<version>1.0.34-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+7
-1
@@ -26,6 +26,7 @@ import org.jkiss.dbeaver.model.auth.AuthPropertyEncryption;
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
|
||||
import org.jkiss.dbeaver.model.auth.SMSession;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -74,7 +75,12 @@ public class LocalAuthProvider implements SMAuthProvider<LocalAuthSession> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalAuthSession openSession(@NotNull DBRProgressMonitor monitor, @NotNull SMSession mainSession, @NotNull Map<String, Object> providerConfig, @NotNull Map<String, Object> userCredentials) throws DBException {
|
||||
public LocalAuthSession openSession(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull SMSession mainSession,
|
||||
@Nullable SMAuthProviderCustomConfiguration customConfiguration,
|
||||
@NotNull Map<String, Object> userCredentials
|
||||
) throws DBException {
|
||||
String userName = CommonUtils.toString(userCredentials.get(CRED_USER));
|
||||
if (CommonUtils.isEmpty(userName)) {
|
||||
throw new DBException("Invalid user name");
|
||||
|
||||
+44
-8
@@ -27,9 +27,14 @@ import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPObject;
|
||||
import org.jkiss.dbeaver.model.auth.SMSession;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.model.security.SMStandardMeta;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class RPAuthProvider implements SMAuthProviderExternal<SMSession> {
|
||||
@@ -38,15 +43,19 @@ public class RPAuthProvider implements SMAuthProviderExternal<SMSession> {
|
||||
|
||||
public static final String X_USER = "X-User";
|
||||
public static final String X_ROLE = "X-Role";
|
||||
public static final String X_FIRST_NAME = "X-First-name";
|
||||
public static final String X_LAST_NAME = "X-Last-name";
|
||||
public static final String AUTH_PROVIDER = "reverseProxy";
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String validateLocalAuth(@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull SMController securityController,
|
||||
@NotNull Map<String, Object> providerConfig,
|
||||
@NotNull Map<String, Object> userCredentials,
|
||||
@Nullable String activeUserId) throws DBException {
|
||||
public String validateLocalAuth(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull SMController securityController,
|
||||
@NotNull Map<String, Object> providerConfig,
|
||||
@NotNull Map<String, Object> userCredentials,
|
||||
@Nullable String activeUserId
|
||||
) throws DBException {
|
||||
String userName = (String) userCredentials.get("user");
|
||||
|
||||
if (activeUserId == null) {
|
||||
@@ -58,9 +67,31 @@ public class RPAuthProvider implements SMAuthProviderExternal<SMSession> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBWUserIdentity getUserIdentity(@NotNull DBRProgressMonitor monitor, @NotNull Map<String, Object> providerConfig, @NotNull Map<String, Object> authParameters) throws DBException {
|
||||
public DBWUserIdentity getUserIdentity(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@Nullable SMAuthProviderCustomConfiguration customConfiguration,
|
||||
@NotNull Map<String, Object> authParameters
|
||||
) throws DBException {
|
||||
String userName = String.valueOf(authParameters.get("user"));
|
||||
return new DBWUserIdentity(userName, userName);
|
||||
StringBuilder nameBuilder = new StringBuilder();
|
||||
Map<String, String> userMeta = new HashMap<>();
|
||||
String firstName = JSONUtils.getString(authParameters, SMStandardMeta.META_FIRST_NAME);
|
||||
String lastName = JSONUtils.getString(authParameters, SMStandardMeta.META_LAST_NAME);
|
||||
if (CommonUtils.isNotEmpty(firstName)) {
|
||||
nameBuilder.append(firstName);
|
||||
userMeta.put(SMStandardMeta.META_FIRST_NAME, firstName);
|
||||
}
|
||||
|
||||
if (CommonUtils.isNotEmpty(lastName)) {
|
||||
nameBuilder.append(lastName);
|
||||
userMeta.put(SMStandardMeta.META_LAST_NAME, lastName);
|
||||
}
|
||||
|
||||
return new DBWUserIdentity(
|
||||
userName,
|
||||
nameBuilder.length() > 0 ? nameBuilder.toString() : userName,
|
||||
userMeta
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -70,7 +101,12 @@ public class RPAuthProvider implements SMAuthProviderExternal<SMSession> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SMSession openSession(@NotNull DBRProgressMonitor monitor, @NotNull SMSession mainSession, @NotNull Map<String, Object> providerConfig, @NotNull Map<String, Object> userCredentials) throws DBException {
|
||||
public SMSession openSession(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull SMSession mainSession,
|
||||
@Nullable SMAuthProviderCustomConfiguration customConfiguration,
|
||||
@NotNull Map<String, Object> userCredentials
|
||||
) throws DBException {
|
||||
return new LocalAuthSession(mainSession, (String) userCredentials.get("user"));
|
||||
}
|
||||
|
||||
|
||||
+187
-40
@@ -37,6 +37,7 @@ 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.DBPPage;
|
||||
import org.jkiss.dbeaver.model.auth.*;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils;
|
||||
@@ -49,6 +50,7 @@ import org.jkiss.dbeaver.model.security.exception.SMAccessTokenExpiredException;
|
||||
import org.jkiss.dbeaver.model.security.exception.SMException;
|
||||
import org.jkiss.dbeaver.model.security.exception.SMRefreshTokenExpiredException;
|
||||
import org.jkiss.dbeaver.model.security.user.*;
|
||||
import org.jkiss.dbeaver.model.sql.SQLUtils;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.SecurityUtils;
|
||||
@@ -125,6 +127,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
if (isSubjectExists(userId)) {
|
||||
throw new DBCException("User or team '" + userId + "' already exists");
|
||||
}
|
||||
log.debug("Create user: " + userId);
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
createAuthSubject(dbCon, userId, SUBJECT_USER);
|
||||
@@ -154,6 +157,17 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importUsers(@NotNull SMUserImportList userImportList) throws DBException {
|
||||
for (SMUserProvisioning user : userImportList.getUsers()) {
|
||||
if (isSubjectExists(user.getUserId())) {
|
||||
log.info("Skip already exist user: " + user.getUserId());
|
||||
continue;
|
||||
}
|
||||
createUser(user.getUserId(), user.getMetaParameters(), true, userImportList.getAuthRole());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUser(String userId) throws DBCException {
|
||||
invalidateAllUserTokens(userId);
|
||||
@@ -205,20 +219,22 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
@NotNull
|
||||
@Override
|
||||
public SMTeam[] getUserTeams(String userId) throws DBException {
|
||||
Map<String, SMTeam> teams = new LinkedHashMap<>();
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
database.normalizeTableNames("SELECT R.* FROM {table_prefix}CB_USER_TEAM UR, {table_prefix}CB_TEAM R " +
|
||||
"WHERE UR.USER_ID=? AND UR.TEAM_ID=R.TEAM_ID"))
|
||||
) {
|
||||
dbStat.setString(1, userId);
|
||||
List<SMTeam> teams = new ArrayList<>();
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
while (dbResult.next()) {
|
||||
teams.add(fetchTeam(dbResult));
|
||||
var team = fetchTeam(dbResult);
|
||||
teams.put(team.getTeamId(), team);
|
||||
}
|
||||
}
|
||||
return teams.toArray(new SMTeam[0]);
|
||||
}
|
||||
readSubjectsMetas(dbCon, SMSubjectType.team, null, teams);
|
||||
return teams.values().toArray(new SMTeam[0]);
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error while reading user teams", e);
|
||||
}
|
||||
@@ -291,19 +307,45 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
return getUserById(getUserIdOrThrow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countUsers(@NotNull SMUserFilter filter) throws DBCException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(
|
||||
"SELECT COUNT(*) FROM {table_prefix}CB_USER" + buildUsersFilter(filter)))) {
|
||||
setUsersFilterValues(dbStat, filter, 1);
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
if (dbResult.next()) {
|
||||
return dbResult.getInt(1);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error while counting users", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMUser[] findUsers(String userNameMask) throws DBCException {
|
||||
return findUsers(new SMUserFilter(userNameMask, null, new DBPPage(0, Integer.MAX_VALUE)));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMUser[] findUsers(@NotNull SMUserFilter filter)
|
||||
throws DBCException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
Map<String, SMUser> result = new LinkedHashMap<>();
|
||||
// Read users
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
database.normalizeTableNames("SELECT USER_ID,IS_ACTIVE,DEFAULT_AUTH_ROLE FROM {table_prefix}CB_USER" +
|
||||
(CommonUtils.isEmpty(userNameMask) ? "\nORDER BY USER_ID" : " WHERE USER_ID=?")))
|
||||
) {
|
||||
if (!CommonUtils.isEmpty(userNameMask)) {
|
||||
dbStat.setString(1, userNameMask);
|
||||
}
|
||||
database.normalizeTableNames("SELECT USER_ID,IS_ACTIVE,DEFAULT_AUTH_ROLE FROM {table_prefix}CB_USER"
|
||||
+ buildUsersFilter(filter) + "\nORDER BY USER_ID LIMIT ? OFFSET ?"))) {
|
||||
int parameterIndex = setUsersFilterValues(dbStat, filter, 1);
|
||||
dbStat.setInt(parameterIndex++, filter.getPage().getLimit());
|
||||
dbStat.setInt(parameterIndex++, filter.getPage().getOffset());
|
||||
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
while (dbResult.next()) {
|
||||
String userId = dbResult.getString(1);
|
||||
@@ -313,14 +355,22 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
}
|
||||
}
|
||||
}
|
||||
readSubjectsMetas(dbCon, SMSubjectType.user, userNameMask, result);
|
||||
if (result.isEmpty()) {
|
||||
return new SMUser[0];
|
||||
}
|
||||
|
||||
readSubjectsMetas(dbCon, SMSubjectType.user, filter.getUserIdMask(), result);
|
||||
StringBuilder teamsSql = new StringBuilder()
|
||||
.append("SELECT USER_ID,TEAM_ID FROM {table_prefix}CB_USER_TEAM")
|
||||
.append("\n")
|
||||
.append("WHERE USER_ID IN (")
|
||||
.append(SQLUtils.generateParamList(result.size()))
|
||||
.append(")");
|
||||
// Read teams
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
database.normalizeTableNames("SELECT USER_ID,TEAM_ID FROM {table_prefix}CB_USER_TEAM" +
|
||||
(CommonUtils.isEmpty(userNameMask) ? "" : " WHERE USER_ID=?")))
|
||||
) {
|
||||
if (!CommonUtils.isEmpty(userNameMask)) {
|
||||
dbStat.setString(1, userNameMask);
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(teamsSql.toString()))) {
|
||||
int parameterIndex = 1;
|
||||
for (String userId : result.keySet()) {
|
||||
dbStat.setString(parameterIndex++, userId);
|
||||
}
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
while (dbResult.next()) {
|
||||
@@ -340,6 +390,33 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
}
|
||||
}
|
||||
|
||||
private String buildUsersFilter(SMUserFilter filter) {
|
||||
StringBuilder where = new StringBuilder();
|
||||
List<String> whereParts = new ArrayList<>();
|
||||
if (!CommonUtils.isEmpty(filter.getUserIdMask())) {
|
||||
whereParts.add("USER_ID LIKE ?");
|
||||
}
|
||||
if (filter.getEnabledState() != null) {
|
||||
whereParts.add("IS_ACTIVE=?");
|
||||
}
|
||||
if (whereParts.size() > 0) {
|
||||
where.append(whereParts.stream().collect(Collectors.joining(" AND ", " WHERE ", "")));
|
||||
}
|
||||
return where.toString();
|
||||
}
|
||||
|
||||
private int setUsersFilterValues(PreparedStatement dbStat, SMUserFilter filter, int parameterIndex)
|
||||
throws SQLException {
|
||||
if (!CommonUtils.isEmpty(filter.getUserIdMask())) {
|
||||
dbStat.setString(parameterIndex++, "%" + filter.getUserIdMask() + "%");
|
||||
}
|
||||
if (filter.getEnabledState() != null) {
|
||||
dbStat.setString(parameterIndex++, filter.getEnabledState() ? CHAR_BOOL_TRUE : CHAR_BOOL_FALSE);
|
||||
}
|
||||
|
||||
return parameterIndex;
|
||||
}
|
||||
|
||||
private void cleanupSubjectMeta(Connection dbCon, String subjectId) throws SQLException {
|
||||
// Delete old metas
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
@@ -367,17 +444,18 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
}
|
||||
}
|
||||
|
||||
private void readSubjectsMetas(Connection dbCon, SMSubjectType subjectType, String nameMask, Map<String, ? extends SMSubject> result) throws SQLException {
|
||||
private void readSubjectsMetas(Connection dbCon, SMSubjectType subjectType, String userIdMask,
|
||||
Map<String, ? extends SMSubject> result) throws SQLException {
|
||||
// Read metas
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
database.normalizeTableNames("SELECT m.SUBJECT_ID,m.META_ID,m.META_VALUE FROM {table_prefix}CB_AUTH_SUBJECT s, " +
|
||||
"{table_prefix}CB_SUBJECT_META m\n" +
|
||||
"WHERE s.SUBJECT_TYPE=? AND s.SUBJECT_ID=m.SUBJECT_ID" +
|
||||
(CommonUtils.isEmpty(nameMask) ? "" : " AND s.SUBJECT_ID=?")))
|
||||
(CommonUtils.isEmpty(userIdMask) ? "" : " AND s.SUBJECT_ID LIKE ?")))
|
||||
) {
|
||||
dbStat.setString(1, subjectType.getCode());
|
||||
if (!CommonUtils.isEmpty(nameMask)) {
|
||||
dbStat.setString(2, nameMask);
|
||||
if (!CommonUtils.isEmpty(userIdMask)) {
|
||||
dbStat.setString(2, "%" + userIdMask + "%");
|
||||
}
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
while (dbResult.next()) {
|
||||
@@ -604,6 +682,20 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUserCredentials(@NotNull String userId, @NotNull String authProviderId) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
JDBCUtils.executeStatement(
|
||||
dbCon,
|
||||
database.normalizeTableNames("DELETE FROM {table_prefix}CB_USER_CREDENTIALS WHERE USER_ID=? AND PROVIDER_ID=?"),
|
||||
userId,
|
||||
authProviderId
|
||||
);
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error deleting user credentials", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String findUserByCredentials(WebAuthProviderDescriptor authProvider, Map<String, Object> authParameters) throws DBCException {
|
||||
Map<String, Object> identCredentials = new LinkedHashMap<>();
|
||||
@@ -1093,7 +1185,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
var smTokens = generateNewSessionToken(smSessionId, null, null, dbCon);
|
||||
var permissions = getAnonymousUserPermissions();
|
||||
txn.commit();
|
||||
return SMAuthInfo.success(
|
||||
return SMAuthInfo.successMainSession(
|
||||
UUID.randomUUID().toString(),
|
||||
smTokens.getSmAccessToken(),
|
||||
smTokens.getSmRefreshToken(),
|
||||
@@ -1125,12 +1217,22 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
var authProgressMonitor = new LoggingProgressMonitor(log);
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
boolean isMainSession = previousSmSessionId == null;
|
||||
Map<String, Object> securedUserIdentifyingCredentials = userCredentials;
|
||||
WebAuthProviderDescriptor authProviderDescriptor = getAuthProvider(authProviderId);
|
||||
var authProviderInstance = authProviderDescriptor.getInstance();
|
||||
|
||||
SMAuthProviderCustomConfiguration providerConfig = authProviderConfigurationId == null
|
||||
? null
|
||||
: application.getAuthConfiguration().getAuthProviderConfiguration(authProviderConfigurationId);
|
||||
|
||||
if (SMAuthProviderExternal.class.isAssignableFrom(authProviderInstance.getClass())) {
|
||||
var authProviderExternal = (SMAuthProviderExternal<?>) authProviderInstance;
|
||||
securedUserIdentifyingCredentials = authProviderExternal.authExternalUser(authProgressMonitor, Map.of(), userCredentials);
|
||||
securedUserIdentifyingCredentials = authProviderExternal.authExternalUser(
|
||||
authProgressMonitor,
|
||||
providerConfig,
|
||||
userCredentials
|
||||
);
|
||||
}
|
||||
|
||||
var filteredUserCreds = filterSecuredUserData(
|
||||
@@ -1146,7 +1248,8 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
appSessionId,
|
||||
previousSmSessionId,
|
||||
sessionType,
|
||||
sessionParameters
|
||||
sessionParameters,
|
||||
isMainSession
|
||||
);
|
||||
|
||||
if (SMAuthProviderFederated.class.isAssignableFrom(authProviderInstance.getClass())) {
|
||||
@@ -1162,7 +1265,8 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
return finishAuthentication(
|
||||
SMAuthInfo.inProgress(
|
||||
authAttemptId,
|
||||
null, Map.of(new SMAuthConfigurationReference(authProviderId, null), securedUserIdentifyingCredentials)
|
||||
null,
|
||||
Map.of(new SMAuthConfigurationReference(authProviderId, null), securedUserIdentifyingCredentials)
|
||||
),
|
||||
true,
|
||||
false
|
||||
@@ -1196,7 +1300,8 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
String appSessionId,
|
||||
String prevSessionId,
|
||||
SMSessionType sessionType,
|
||||
Map<String, Object> sessionParameters
|
||||
Map<String, Object> sessionParameters,
|
||||
boolean isMainSession
|
||||
) throws DBException {
|
||||
String authAttemptId = UUID.randomUUID().toString();
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
@@ -1399,7 +1504,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
SMTokens smTokens = findTokenBySmSession(smSessionId);
|
||||
SMAuthPermissions authPermissions = getTokenPermissions(smTokens.getSmAccessToken());
|
||||
String authRole = readTokenAuthRole(smTokens.getSmAccessToken());
|
||||
var successAuthStatus = SMAuthInfo.success(
|
||||
var successAuthStatus = SMAuthInfo.successMainSession(
|
||||
authId,
|
||||
smTokens.getSmAccessToken(),
|
||||
smTokens.getSmRefreshToken(),
|
||||
@@ -1427,7 +1532,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
for (SMAuthInfo authData : allLatestAuthData) {
|
||||
mergedData.putAll(authData.getAuthData());
|
||||
}
|
||||
return SMAuthInfo.success(
|
||||
return SMAuthInfo.successMainSession(
|
||||
"restore_session_attempt_" + UUID.randomUUID(),
|
||||
latestActiveSmTokens.getAccessToken(),
|
||||
latestActiveSmTokens.getRefreshToken(),
|
||||
@@ -1507,6 +1612,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
return currentUserCreds;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private SMTokens findTokenBySmSession(String smSessionId) throws DBException {
|
||||
try (Connection dbCon = database.openConnection();
|
||||
PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
@@ -1607,12 +1713,13 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
|
||||
SMTokens smTokens = null;
|
||||
SMAuthPermissions permissions = null;
|
||||
String activeUserId = null;
|
||||
if (!isMainAuthSession) {
|
||||
var accessToken = findTokenBySmSession(authAttemptSessionInfo.getSmSessionId()).getSmAccessToken();
|
||||
//this is an additional authorization and we should to return the original permissions and userId
|
||||
smTokens = findTokenBySmSession(authAttemptSessionInfo.getSmSessionId());
|
||||
permissions = getTokenPermissions(smTokens.getSmAccessToken());
|
||||
permissions = getTokenPermissions(accessToken);
|
||||
activeUserId = permissions.getUserId();
|
||||
}
|
||||
String activeUserId = permissions == null ? null : permissions.getUserId();
|
||||
|
||||
Map<SMAuthConfigurationReference, Object> storedUserData = new LinkedHashMap<>();
|
||||
SMTeam[] allTeams = null;
|
||||
@@ -1678,7 +1785,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
}
|
||||
|
||||
String tokenAuthRole = updateUserAuthRoleIfNeeded(activeUserId, detectedAuthRole);
|
||||
if (smTokens == null && permissions == null) {
|
||||
if (isMainAuthSession) {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
String smSessionId;
|
||||
@@ -1708,15 +1815,24 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
}
|
||||
var authStatus = forceExpireAuthAfterSuccess ? SMAuthStatus.EXPIRED : SMAuthStatus.SUCCESS;
|
||||
updateAuthStatus(authId, authStatus, storedUserData, null, permissions.getSessionId());
|
||||
return SMAuthInfo.success(
|
||||
authId,
|
||||
smTokens.getSmAccessToken(),
|
||||
//refresh token must be sent only once
|
||||
isMainAuthSession ? smTokens.getSmRefreshToken() : null,
|
||||
permissions,
|
||||
authInfo.getAuthData(),
|
||||
tokenAuthRole
|
||||
);
|
||||
|
||||
if (isMainAuthSession) {
|
||||
return SMAuthInfo.successMainSession(
|
||||
authId,
|
||||
smTokens.getSmAccessToken(),
|
||||
//refresh token must be sent only from main session
|
||||
smTokens.getSmRefreshToken(),
|
||||
permissions,
|
||||
authInfo.getAuthData(),
|
||||
tokenAuthRole
|
||||
);
|
||||
} else {
|
||||
return SMAuthInfo.successChildSession(
|
||||
authId,
|
||||
permissions,
|
||||
authInfo.getAuthData()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void autoUpdateUserTeams(
|
||||
@@ -1967,6 +2083,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
return getTokenPermissions(activeUserCredentials.getSmAccessToken());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private SMAuthPermissions getTokenPermissions(@NotNull String token) throws DBException {
|
||||
String userId;
|
||||
String sessionId;
|
||||
@@ -2074,6 +2191,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
if (CommonUtils.isEmpty(subjectIds) || CommonUtils.isEmpty(objectIds)) {
|
||||
return;
|
||||
}
|
||||
Set<String> filteredSubjects = getFilteredSubjects(subjectIds);
|
||||
// validatePermissions(objectType.getObjectType(), permissions);
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
@@ -2096,6 +2214,10 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
dbStat.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
|
||||
dbStat.setString(4, grantor);
|
||||
for (String subjectId : subjectIds) {
|
||||
if (!filteredSubjects.contains(subjectId)) {
|
||||
log.error("Subject '" + subjectId + "' is not found in database");
|
||||
continue;
|
||||
}
|
||||
dbStat.setString(5, subjectId);
|
||||
for (String permission : permissions) {
|
||||
dbStat.setString(6, permission);
|
||||
@@ -2427,4 +2549,29 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
throw new DBCException("Error deleting auth attempt info", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getFilteredSubjects(Set<String> allSubjects) {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
Set<String> result = new HashSet<>();
|
||||
var sqlBuilder = new StringBuilder("SELECT SUBJECT_ID FROM {table_prefix}CB_AUTH_SUBJECT U ")
|
||||
.append("WHERE SUBJECT_ID IN (")
|
||||
.append(SQLUtils.generateParamList(allSubjects.size()))
|
||||
.append(")");
|
||||
try (var dbStat = dbCon.prepareStatement(database.normalizeTableNames(sqlBuilder.toString()))) {
|
||||
int parameterIndex = 1;
|
||||
for (String subjectId : allSubjects) {
|
||||
dbStat.setString(parameterIndex++, subjectId);
|
||||
}
|
||||
try (ResultSet dbResult = dbStat.executeQuery()) {
|
||||
while (dbResult.next()) {
|
||||
result.add(dbResult.getString(1));
|
||||
}
|
||||
}
|
||||
};
|
||||
return result;
|
||||
} catch (SQLException e) {
|
||||
log.error("Error getting all subject ids from database", e);
|
||||
return Set.of();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-8
@@ -60,6 +60,7 @@ import java.net.InetAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Database management
|
||||
@@ -275,7 +276,7 @@ public class CBDatabase {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
CBDatabaseInitialData getInitialData() {
|
||||
CBDatabaseInitialData getInitialData() throws DBException {
|
||||
String initialDataPath = databaseConfiguration.getInitialDataConfiguration();
|
||||
if (CommonUtils.isEmpty(initialDataPath)) {
|
||||
return null;
|
||||
@@ -287,8 +288,7 @@ public class CBDatabase {
|
||||
Gson gson = new GsonBuilder().setLenient().create();
|
||||
return gson.fromJson(reader, CBDatabaseInitialData.class);
|
||||
} catch (Exception e) {
|
||||
log.error("Error loading initial data configuration", e);
|
||||
return null;
|
||||
throw new DBException("Error loading initial data configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,15 +412,26 @@ public class CBDatabase {
|
||||
|
||||
String adminName = initialData.getAdminName();
|
||||
String adminPassword = initialData.getAdminPassword();
|
||||
|
||||
if (!CommonUtils.isEmpty(initialData.getTeams())) {
|
||||
List<SMTeam> initialTeams = initialData.getTeams();
|
||||
String defaultTeam = application.getAppConfiguration().getDefaultUserTeam();
|
||||
if (CommonUtils.isNotEmpty(defaultTeam)) {
|
||||
Set<String> initialTeamNames = initialTeams == null
|
||||
? Set.of()
|
||||
: initialTeams.stream().map(SMTeam::getTeamId).collect(Collectors.toSet());
|
||||
if (!initialTeamNames.contains(defaultTeam)) {
|
||||
throw new DBException("Initial teams configuration doesn't contain default team " + defaultTeam);
|
||||
}
|
||||
}
|
||||
if (!CommonUtils.isEmpty(initialTeams)) {
|
||||
// Create teams
|
||||
for (SMTeam team : initialData.getTeams()) {
|
||||
for (SMTeam team : initialTeams) {
|
||||
adminSecurityController.createTeam(team.getTeamId(), team.getName(), team.getDescription(), adminName);
|
||||
if (adminName != null && !application.isMultiNode()) {
|
||||
adminSecurityController.setSubjectPermissions(team.getTeamId(),
|
||||
adminSecurityController.setSubjectPermissions(
|
||||
team.getTeamId(),
|
||||
new ArrayList<>(team.getPermissions()),
|
||||
adminName);
|
||||
adminName
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>io.trino</groupId>
|
||||
<artifactId>trino-jdbc</artifactId>
|
||||
<version>351</version>
|
||||
<version>422</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.ce.drivers.feature"
|
||||
label="Cloudbeaver CE 3rd Party Drivers"
|
||||
version="1.0.100.qualifier"
|
||||
version="1.0.102.qualifier"
|
||||
provider-name="DBeaver Corp">
|
||||
|
||||
<plugin id="io.cloudbeaver.resources.drivers.base" download-size="0" install-size="0" version="0.0.0"/>
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.ce.drivers.feature</artifactId>
|
||||
<version>1.0.100-SNAPSHOT</version>
|
||||
<version>1.0.102-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.product.ce.feature"
|
||||
label="%featureName"
|
||||
version="23.1.2.qualifier"
|
||||
version="23.1.4.qualifier"
|
||||
provider-name="%providerName"
|
||||
plugin="io.cloudbeaver.product.ce">
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce.feature</artifactId>
|
||||
<version>23.1.2-SNAPSHOT</version>
|
||||
<version>23.1.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
|
||||
<build>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user