mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 06:40:05 +08:00
Merge branch 'devel' of https://github.com/dbeaver/cloudbeaver into devel
This commit is contained in:
@@ -44,6 +44,7 @@
|
||||
|
||||
resourceQuotas: {
|
||||
dataExportFileSizeLimit: 10000000,
|
||||
resourceManagerFileSizeLimit: 500000,
|
||||
sqlMaxRunningQueries: 100,
|
||||
sqlResultSetRowsLimit: 100000,
|
||||
sqlResultSetMemoryLimit: 2000000,
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
|
||||
resourceQuotas: {
|
||||
dataExportFileSizeLimit: 10000000,
|
||||
resourceManagerFileSizeLimit: 500000,
|
||||
sqlMaxRunningQueries: 100,
|
||||
sqlResultSetRowsLimit: 100000,
|
||||
sqlResultSetMemoryLimit: 2000000,
|
||||
|
||||
+2
-15
@@ -16,9 +16,6 @@
|
||||
*/
|
||||
package io.cloudbeaver.auth;
|
||||
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
|
||||
import org.jkiss.dbeaver.model.auth.SMSession;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import io.cloudbeaver.DBWUserIdentity;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.user.WebUser;
|
||||
@@ -26,6 +23,8 @@ import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
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 java.util.Map;
|
||||
@@ -46,18 +45,6 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
|
||||
@NotNull Map<String, Object> authParameters // Passed auth parameters (e.g. user name or password)
|
||||
) throws DBException;
|
||||
|
||||
/**
|
||||
* Validates that external user may be associated with local user
|
||||
* @param userCredentials credentials from authExternalUser
|
||||
* @return new user ID. If activeUser is not null then it must be the same as activeUser ID.
|
||||
*/
|
||||
@NotNull
|
||||
String validateLocalAuth(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull SMController securityController,
|
||||
@NotNull Map<String, Object> providerConfig,
|
||||
@NotNull Map<String, Object> userCredentials,
|
||||
@Nullable WebUser activeUser) throws DBException;
|
||||
|
||||
@NotNull
|
||||
DBWUserIdentity getUserIdentity(
|
||||
|
||||
+6
@@ -53,6 +53,12 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
return new EclipseWorkspaceImpl(platform, eclipseWorkspace);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Path getDefaultWorkingFolder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Path loadServerConfiguration() {
|
||||
String configPath = DEFAULT_CONFIG_FILE_PATH;
|
||||
|
||||
+13
@@ -17,6 +17,8 @@
|
||||
package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.service.sql.WebSQLConstants;
|
||||
import io.cloudbeaver.utils.WebAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -28,6 +30,7 @@ import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMResource;
|
||||
import org.jkiss.dbeaver.model.rm.RMResourceChange;
|
||||
import org.jkiss.dbeaver.model.sql.DBQuotaException;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -225,6 +228,16 @@ public class LocalResourceController implements RMController {
|
||||
@NotNull String resourcePath,
|
||||
@NotNull byte[] data) throws DBException
|
||||
{
|
||||
Number fileSizeLimit = WebAppUtils.getWebApplication()
|
||||
.getAppConfiguration()
|
||||
.getResourceQuota(WebSQLConstants.QUOTA_PROP_RM_FILE_SIZE_LIMIT);
|
||||
if (fileSizeLimit != null && data.length > fileSizeLimit.longValue()) {
|
||||
throw new DBQuotaException(
|
||||
"File size quota exceeded",
|
||||
WebSQLConstants.QUOTA_PROP_RM_FILE_SIZE_LIMIT,
|
||||
fileSizeLimit.longValue(),
|
||||
data.length);
|
||||
}
|
||||
Path targetPath = getTargetPath(projectId, resourcePath);
|
||||
if (!Files.exists(targetPath)) {
|
||||
throw new DBException("Resource '" + resourcePath + "' doesn't exists");
|
||||
|
||||
+7
-6
@@ -80,7 +80,7 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
|
||||
private static final Log log = Log.getLog(WebSession.class);
|
||||
|
||||
public static final SMSessionType CB_SESSION_TYPE = new SMSessionType("Cloudbeaver");
|
||||
public static final SMSessionType CB_SESSION_TYPE = new SMSessionType("CloudBeaver");
|
||||
|
||||
private static final String ATTR_LOCALE = "locale";
|
||||
|
||||
@@ -215,7 +215,7 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
return user;
|
||||
}
|
||||
|
||||
private synchronized String getUserId() {
|
||||
public synchronized String getUserId() {
|
||||
return user == null ? null : user.getUserId();
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
return Arrays.stream(securityController
|
||||
.getSubjectConnectionAccess(new String[]{subjectId}))
|
||||
.map(SMDataSourceGrant::getDataSourceId).collect(Collectors.toSet());
|
||||
} catch (DBCException e) {
|
||||
} catch (DBException e) {
|
||||
addSessionError(e);
|
||||
log.error("Error reading connection grants", e);
|
||||
return Collections.emptySet();
|
||||
@@ -425,12 +425,12 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
this.accessibleConnectionIds = readAccessibleConnectionIds();
|
||||
}
|
||||
|
||||
private synchronized void authAsAnonymousUser() throws DBCException {
|
||||
private synchronized void authAsAnonymousUser() throws DBException {
|
||||
if (!application.getAppConfiguration().isAnonymousAccessEnabled()) {
|
||||
this.sessionPermissions = Collections.emptySet();
|
||||
return;
|
||||
}
|
||||
var authInfo = securityController.authenticateAnonymousUser(this.id, getSessionParameters(), CB_SESSION_TYPE);
|
||||
SMAuthInfo authInfo = securityController.authenticateAnonymousUser(this.id, getSessionParameters(), CB_SESSION_TYPE);
|
||||
updateSMAuthInfo(authInfo);
|
||||
}
|
||||
|
||||
@@ -893,7 +893,7 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
this.adminSecurityController = null;
|
||||
}
|
||||
|
||||
public synchronized void updateSMAuthInfo(SMAuthInfo smAuthInfo) throws DBCException {
|
||||
public synchronized void updateSMAuthInfo(SMAuthInfo smAuthInfo) throws DBException {
|
||||
var isNonAnonymousUserAuthorized = isAuthorizedInSecurityManager() && getUser() != null;
|
||||
if (isNonAnonymousUserAuthorized && !Objects.equals(getUserId(), smAuthInfo.getUserId())) {
|
||||
throw new DBCException("Another user is already logged in");
|
||||
@@ -904,6 +904,7 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
this.adminSecurityController = application.getAdminSecurityController(this);
|
||||
|
||||
this.user = smAuthInfo.getUserId() == null ? null : new WebUser(securityController.getUserById(smAuthInfo.getUserId()));
|
||||
|
||||
refreshUserData();
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -26,6 +26,7 @@ public class WebSQLConstants {
|
||||
public static final String QUOTA_PROP_QUERY_LIMIT = "sqlMaxRunningQueries";
|
||||
public static final String QUOTA_PROP_TEXT_PREVIEW_MAX_LENGTH = "sqlTextPreviewMaxLength";
|
||||
public static final String QUOTA_PROP_BINARY_PREVIEW_MAX_LENGTH = "sqlBinaryPreviewMaxLength";
|
||||
public static final String QUOTA_PROP_RM_FILE_SIZE_LIMIT = "resourceManagerFileSizeLimit";
|
||||
|
||||
public static final int TEXT_PREVIEW_MAX_LENGTH = 4 * 1024;
|
||||
public static final int BINARY_PREVIEW_MAX_LENGTH = 255 * 1024;
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@
|
||||
package io.cloudbeaver.service.admin;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.security.SMDataSourceGrant;
|
||||
@@ -59,7 +60,7 @@ public class AdminRoleInfo {
|
||||
}
|
||||
|
||||
@Property
|
||||
public SMDataSourceGrant[] getGrantedConnections() throws DBCException {
|
||||
public SMDataSourceGrant[] getGrantedConnections() throws DBException {
|
||||
return session.getSecurityController().getSubjectConnectionAccess(new String[]{getRoleId()});
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -20,8 +20,8 @@ import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.user.WebUser;
|
||||
import io.cloudbeaver.model.user.WebUserOriginInfo;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.security.SMDataSourceGrant;
|
||||
import org.jkiss.dbeaver.model.security.user.SMRole;
|
||||
@@ -65,7 +65,7 @@ public class AdminUserInfo {
|
||||
}
|
||||
|
||||
@Property
|
||||
public String[] getGrantedRoles() throws DBCException {
|
||||
public String[] getGrantedRoles() throws DBException {
|
||||
if (user.getRoles() == null) {
|
||||
SMRole[] userRoles = session.getSecurityController().getUserRoles(getUserId());
|
||||
user.setRoles(userRoles);
|
||||
@@ -74,7 +74,7 @@ public class AdminUserInfo {
|
||||
}
|
||||
|
||||
@Property
|
||||
public SMDataSourceGrant[] getGrantedConnections() throws DBCException {
|
||||
public SMDataSourceGrant[] getGrantedConnections() throws DBException {
|
||||
return session.getSecurityController().getSubjectConnectionAccess(new String[]{getUserId()});
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public class AdminUserInfo {
|
||||
}
|
||||
try {
|
||||
userLinkedProviders = session.getSecurityController().getUserLinkedProviders(user.getUserId());
|
||||
} catch (DBCException e) {
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error reading user linked providers", e);
|
||||
}
|
||||
return userLinkedProviders;
|
||||
|
||||
+4
-4
@@ -376,7 +376,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
|
||||
try {
|
||||
webSession.getSecurityController().setConnectionSubjectAccess(id, null, null);
|
||||
} catch (DBCException e) {
|
||||
} catch (DBException e) {
|
||||
log.error(e);
|
||||
}
|
||||
return true;
|
||||
@@ -560,7 +560,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
public SMDataSourceGrant[] getConnectionSubjectAccess(WebSession webSession, String connectionId) throws DBWebException {
|
||||
try {
|
||||
return webSession.getSecurityController().getConnectionSubjectAccess(connectionId);
|
||||
} catch (DBCException e) {
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error getting connection access info", e);
|
||||
}
|
||||
}
|
||||
@@ -577,7 +577,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
}
|
||||
try {
|
||||
webSession.getSecurityController().setConnectionSubjectAccess(connectionId, subjects.toArray(new String[0]), grantor.getUserId());
|
||||
} catch (DBCException e) {
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error setting connection subject access", e);
|
||||
}
|
||||
return true;
|
||||
@@ -587,7 +587,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
|
||||
public SMDataSourceGrant[] getSubjectConnectionAccess(@NotNull WebSession webSession, @NotNull String subjectId) throws DBWebException {
|
||||
try {
|
||||
return webSession.getSecurityController().getSubjectConnectionAccess(new String[]{subjectId});
|
||||
} catch (DBCException e) {
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error getting connection access info", e);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ public class RPSessionHandler implements DBWSessionHandler {
|
||||
Map<String, Object> sessionParameters = webSession.getSessionParameters();
|
||||
sessionParameters.put(SMConstants.SESSION_PARAM_TRUSTED_USER_ROLES, userRoles);
|
||||
Map<String, Object> userCredentials = authProviderExternal.authExternalUser(
|
||||
webSession.getProgressMonitor(), sessionParameters, credentials);
|
||||
webSession.getProgressMonitor(), sessionParameters, credentials);
|
||||
try {
|
||||
SMAuthInfo smAuthInfo = securityController.authenticate(
|
||||
webSession.getSessionId(), sessionParameters,
|
||||
|
||||
+2
-2
@@ -19,6 +19,7 @@ package io.cloudbeaver.service.auth;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.user.WebUser;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
@@ -65,9 +66,8 @@ public class WebUserInfo {
|
||||
public List<String> getLinkedAuthProviders() throws DBWebException {
|
||||
if (linkedProviders == null) {
|
||||
try {
|
||||
|
||||
linkedProviders = session.getSecurityController().getUserLinkedProviders(session.getUser().getUserId());
|
||||
} catch (DBCException e) {
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error reading user linked providers", e);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -101,7 +101,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
linkWithActiveUser = true;
|
||||
}
|
||||
} else if (!providerEnabled) {
|
||||
if (!isAdminAuthTry(webSession, authProvider, userCredentials)) {
|
||||
if (!isAdminAuthTry(webSession, authProvider, authParameters)) {
|
||||
throw new DBWebException("Authentication provider '" + providerId + "' is disabled");
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
securityController,
|
||||
providerConfig,
|
||||
userCredentials,
|
||||
webSession.getUser());
|
||||
webSession.getUserId());
|
||||
} else {
|
||||
userId = "temp_config_admin";
|
||||
}
|
||||
@@ -239,7 +239,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
SMAuthInfo authInfo = securityController.authenticate(session.getSessionId(), session.getSessionParameters(), WebSession.CB_SESSION_TYPE, authProvider.getId(), userCredentials);
|
||||
|
||||
isAdmin = authInfo.getPermissions().contains(DBWConstants.PERMISSION_ADMIN);
|
||||
} catch (DBCException e) {
|
||||
} catch (DBException e) {
|
||||
log.error(e);
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
} else {
|
||||
return new WebUserInfo(webSession, webSession.getUser());
|
||||
}
|
||||
} catch (DBCException e) {
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error reading user details", e);
|
||||
}
|
||||
}
|
||||
@@ -324,7 +324,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
name,
|
||||
value);
|
||||
return true;
|
||||
} catch (DBCException e) {
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException("Error setting user parameter", e);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -82,10 +82,10 @@ public class DBNResourceManagerResource extends DBNNode {
|
||||
|
||||
private String getResourceFolder() {
|
||||
StringBuilder folder = new StringBuilder();
|
||||
for (DBNNode parent = getParentNode(); parent != null; parent = parent.getParentNode()) {
|
||||
for (DBNNode parent = this; parent != null; parent = parent.getParentNode()) {
|
||||
if (parent instanceof DBNResourceManagerResource) {
|
||||
if (folder.length() > 0) folder.append('/');
|
||||
folder.append(parent.getName());
|
||||
if (folder.length() > 0) folder.insert(0, '/');
|
||||
folder.insert(0, parent.getName());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
+21
-7
@@ -18,11 +18,13 @@ package io.cloudbeaver.auth.provider.local;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
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.SMController;
|
||||
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
|
||||
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
@@ -38,17 +40,19 @@ public class LocalAuthProvider implements SMAuthProvider<LocalAuthSession> {
|
||||
public static final String CRED_USER = LocalAuthProviderConstants.CRED_USER;
|
||||
public static final String CRED_PASSWORD = LocalAuthProviderConstants.CRED_PASSWORD;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LocalAuthSession openSession(@NotNull DBRProgressMonitor monitor, @NotNull SMSession mainSession, @NotNull Map<String, Object> providerConfig, @NotNull Map<String, Object> userCredentials) 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 = CommonUtils.toString(userCredentials.get(CRED_USER), null);
|
||||
|
||||
if (!WebSession.class.isAssignableFrom(mainSession.getClass())) {
|
||||
throw new DBException("Invalid parent session, expected web session");
|
||||
}
|
||||
|
||||
var mainWebSession = (WebSession) mainSession;
|
||||
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(PROVIDER_ID);
|
||||
Map<String, Object> storedCredentials = mainWebSession.getSecurityController().getUserCredentials(userName, authProvider.getId());
|
||||
Map<String, Object> storedCredentials = securityController.getUserCredentials(userName, authProvider.getId());
|
||||
if (storedCredentials == null) {
|
||||
throw new DBException("Invalid user name or password");
|
||||
}
|
||||
@@ -65,6 +69,16 @@ public class LocalAuthProvider implements SMAuthProvider<LocalAuthSession> {
|
||||
if (!storedPasswordHash.equals(clientPasswordHash)) {
|
||||
throw new DBException("Invalid user name or password");
|
||||
}
|
||||
|
||||
return activeUserId == null ? userName : activeUserId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalAuthSession openSession(@NotNull DBRProgressMonitor monitor, @NotNull SMSession mainSession, @NotNull Map<String, Object> providerConfig, @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");
|
||||
}
|
||||
return new LocalAuthSession(mainSession, userName);
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -49,13 +49,17 @@ public class RPAuthProvider implements SMAuthProviderExternal<SMSession> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String validateLocalAuth(@NotNull DBRProgressMonitor monitor, @NotNull SMController securityController, @NotNull Map<String, Object> providerConfig, @NotNull Map<String, Object> userCredentials, @Nullable WebUser activeUser) 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 (activeUser == null) {
|
||||
if (activeUserId == null) {
|
||||
return userName;
|
||||
} else {
|
||||
return activeUser.getUserId();
|
||||
return activeUserId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+39
-31
@@ -17,9 +17,7 @@
|
||||
package io.cloudbeaver.service.security.internal;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.auth.SMAuthProviderExternal;
|
||||
import io.cloudbeaver.auth.provider.rp.RPAuthProvider;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.service.security.internal.db.CBDatabase;
|
||||
import io.cloudbeaver.utils.WebAppUtils;
|
||||
@@ -31,6 +29,7 @@ import org.jkiss.dbeaver.model.auth.*;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils;
|
||||
import org.jkiss.dbeaver.model.impl.jdbc.exec.JDBCTransaction;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.*;
|
||||
import org.jkiss.dbeaver.model.security.exception.SMException;
|
||||
@@ -155,7 +154,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMRole[] getUserRoles(String userId) throws DBCException {
|
||||
public SMRole[] getUserRoles(String userId) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
"SELECT R.* FROM CB_USER_ROLE UR,CB_ROLE R " +
|
||||
@@ -175,7 +174,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SMUser getUserById(String userId) throws DBCException {
|
||||
public SMUser getUserById(String userId) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
SMUser user;
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement("SELECT * FROM CB_USER WHERE USER_ID=?")) {
|
||||
@@ -298,7 +297,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserParameter(String userId, String name, Object value) throws DBCException {
|
||||
public void setUserParameter(String userId, String name, Object value) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
if (value == null) {
|
||||
@@ -358,7 +357,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserCredentials(String userId, String authProviderId, Map<String, Object> credentials) throws DBCException {
|
||||
public void setUserCredentials(String userId, String authProviderId, Map<String, Object> credentials) throws DBException {
|
||||
List<String[]> transformedCredentials;
|
||||
AuthProviderDescriptor authProvider = getAuthProvider(authProviderId);
|
||||
try {
|
||||
@@ -495,7 +494,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUserLinkedProviders(String userId) throws DBCException {
|
||||
public String[] getUserLinkedProviders(String userId) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
"SELECT DISTINCT PROVIDER_ID FROM CB_USER_CREDENTIALS\n" +
|
||||
@@ -696,7 +695,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> getSubjectPermissions(String subjectId) throws DBCException {
|
||||
public Set<String> getSubjectPermissions(String subjectId) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
Set<String> permissions = new HashSet<>();
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement("SELECT PERMISSION_ID FROM CB_AUTH_PERMISSIONS WHERE SUBJECT_ID=?")) {
|
||||
@@ -715,7 +714,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> getUserPermissions(String userId) throws DBCException {
|
||||
public Set<String> getUserPermissions(String userId) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
Set<String> permissions = new HashSet<>();
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
@@ -747,7 +746,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
// Sessions
|
||||
|
||||
@Override
|
||||
public boolean isSessionPersisted(String id) throws DBCException {
|
||||
public boolean isSessionPersisted(String id) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
"SELECT 1 FROM CB_SESSION WHERE SESSION_ID=?")) {
|
||||
@@ -764,8 +763,8 @@ public class CBSecurityController implements SMAdminController {
|
||||
}
|
||||
}
|
||||
|
||||
private void createSessionIfNotExist(@NotNull String appSessionId, @Nullable String userId, @NotNull Map<String, Object> parameters, @NotNull SMSessionType sessionType, Connection dbCon) throws SQLException, DBCException {
|
||||
if(isSessionPersisted(appSessionId)) {
|
||||
private void createSessionIfNotExist(@NotNull String appSessionId, @Nullable String userId, @NotNull Map<String, Object> parameters, @NotNull SMSessionType sessionType, Connection dbCon) throws SQLException, DBException {
|
||||
if (isSessionPersisted(appSessionId)) {
|
||||
return;
|
||||
}
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
@@ -798,7 +797,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SMAuthInfo authenticateAnonymousUser(@NotNull String appSessionId, @NotNull Map<String, Object> sessionParameters, @NotNull SMSessionType sessionType) throws DBCException {
|
||||
public SMAuthInfo authenticateAnonymousUser(@NotNull String appSessionId, @NotNull Map<String, Object> sessionParameters, @NotNull SMSessionType sessionType) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
createSessionIfNotExist(appSessionId, null, sessionParameters, sessionType, dbCon);
|
||||
@@ -809,15 +808,18 @@ public class CBSecurityController implements SMAdminController {
|
||||
return new SMAuthInfo(token, null, permissions);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DBCException("Error during auth", e);
|
||||
throw new DBException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SMAuthInfo authenticate(@NotNull String appSessionId, @NotNull Map<String, Object> sessionParameters, @NotNull SMSessionType sessionType, @NotNull String authProviderId, @NotNull Map<String, Object> userCredentials) throws DBCException {
|
||||
public SMAuthInfo authenticate(@NotNull String appSessionId, @NotNull Map<String, Object> sessionParameters, @NotNull SMSessionType sessionType, @NotNull String authProviderId, @NotNull Map<String, Object> userCredentials) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
var userId = findOrCreateExternalUserByCredentials(authProviderId, sessionParameters, userCredentials);
|
||||
var userId = findOrCreateExternalUserByCredentials(authProviderId,
|
||||
sessionParameters,
|
||||
userCredentials,
|
||||
new VoidProgressMonitor());
|
||||
if (userId == null) {
|
||||
throw new SMException("Invalid user credentials");
|
||||
}
|
||||
@@ -827,28 +829,34 @@ public class CBSecurityController implements SMAdminController {
|
||||
txn.commit();
|
||||
return new SMAuthInfo(token, userId, permissions);
|
||||
}
|
||||
} catch (SQLException | DBException e) {
|
||||
throw new DBCException("Error during auth", e);
|
||||
} catch (SQLException e) {
|
||||
throw new DBException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String findOrCreateExternalUserByCredentials(@NotNull String authProviderId,
|
||||
@NotNull Map<String, Object> sessionParameters,
|
||||
@NotNull Map<String, Object> userCredentials) throws DBException {
|
||||
var userId = findUserByCredentials(authProviderId, userCredentials);
|
||||
@NotNull Map<String, Object> userCredentials,
|
||||
@NotNull DBRProgressMonitor progressMonitor) throws DBException {
|
||||
AuthProviderDescriptor authProvider = getAuthProvider(authProviderId);
|
||||
SMAuthProvider<?> smAuthProviderInstance = authProvider.getInstance();
|
||||
String userId = findUserByCredentials(authProviderId, userCredentials);
|
||||
String expectedUserId;
|
||||
try {
|
||||
expectedUserId = smAuthProviderInstance.validateLocalAuth(progressMonitor, this, Map.of(), userCredentials, null);
|
||||
} catch (DBException e) {
|
||||
return null;
|
||||
}
|
||||
if (userId != null && !expectedUserId.equals(userId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (userId == null) {
|
||||
if (!(authProvider.getInstance() instanceof SMAuthProviderExternal<?>)) {
|
||||
return null;
|
||||
}
|
||||
var externalAuthProvider = (SMAuthProviderExternal<?>) authProvider.getInstance();
|
||||
userId = externalAuthProvider.validateLocalAuth(
|
||||
new VoidProgressMonitor(),
|
||||
this,
|
||||
Collections.emptyMap(),
|
||||
userCredentials,
|
||||
null);
|
||||
|
||||
userId = expectedUserId;
|
||||
if (!isSubjectExists(userId)) {
|
||||
var newUser = new SMUser(userId);
|
||||
createUser(newUser.getUserId(), newUser.getMetaParameters());
|
||||
@@ -861,7 +869,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
}
|
||||
if (authProvider.isTrusted()) {
|
||||
if (WebAppUtils.getWebApplication().isMultiNode()) {
|
||||
throw new DBWebException("Authorization through trusted provider is not available in multi node");
|
||||
throw new SMException("Authorization through trusted provider is not available in multi node");
|
||||
}
|
||||
Object reverseProxyUserRoles = sessionParameters.get(SMConstants.SESSION_PARAM_TRUSTED_USER_ROLES);
|
||||
if (reverseProxyUserRoles instanceof List) {
|
||||
@@ -930,7 +938,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMDataSourceGrant[] getSubjectConnectionAccess(@NotNull String[] subjectIds) throws DBCException {
|
||||
public SMDataSourceGrant[] getSubjectConnectionAccess(@NotNull String[] subjectIds) throws DBException {
|
||||
if (subjectIds.length == 0) {
|
||||
return new SMDataSourceGrant[0];
|
||||
}
|
||||
@@ -1005,7 +1013,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMDataSourceGrant[] getConnectionSubjectAccess(String connectionId) throws DBCException {
|
||||
public SMDataSourceGrant[] getConnectionSubjectAccess(String connectionId) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
{
|
||||
try (PreparedStatement dbStat = dbCon.prepareStatement(
|
||||
@@ -1031,7 +1039,7 @@ public class CBSecurityController implements SMAdminController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConnectionSubjectAccess(@NotNull String connectionId, @Nullable String[] subjects, @Nullable String grantorId) throws DBCException {
|
||||
public void setConnectionSubjectAccess(@NotNull String connectionId, @Nullable String[] subjects, @Nullable String grantorId) throws DBException {
|
||||
try (Connection dbCon = database.openConnection()) {
|
||||
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
|
||||
// Delete all permissions
|
||||
|
||||
+2
-1
@@ -242,8 +242,9 @@ public class CBDatabase {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private SMUser createAdminUser(@NotNull String adminName, @Nullable String adminPassword) throws DBCException {
|
||||
private SMUser createAdminUser(@NotNull String adminName, @Nullable String adminPassword) throws DBException {
|
||||
SMUser adminUser = adminSecurityController.getUserById(adminName);
|
||||
|
||||
if (adminUser == null) {
|
||||
adminUser = new SMUser(adminName);
|
||||
adminSecurityController.createUser(adminUser.getUserId(), adminUser.getMetaParameters());
|
||||
|
||||
+4
-2
@@ -10,8 +10,10 @@ import type { Column as DataGridColumn } from 'react-data-grid';
|
||||
|
||||
import type { DBObject } from '@cloudbeaver/core-app';
|
||||
|
||||
export type DataColumn = DataGridColumn<DBObject>;
|
||||
export interface IDataColumn extends DataGridColumn<DBObject> {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface ICustomColumn extends DataColumn {
|
||||
export interface ICustomColumn extends IDataColumn {
|
||||
order?: number;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext } from 'react';
|
||||
import type { HeaderRendererProps } from 'react-data-grid';
|
||||
|
||||
import type { DBObject } from '@cloudbeaver/core-app';
|
||||
|
||||
import { TableContext } from './TableContext';
|
||||
|
||||
export const HeaderRenderer = observer<HeaderRendererProps<DBObject>>(function HeaderRenderer(props) {
|
||||
const tableContext = useContext(TableContext);
|
||||
const dataColumn = tableContext.tableData?.columns.find(column => column.key === props.column.key);
|
||||
|
||||
return (
|
||||
<div title={dataColumn?.description}>
|
||||
{props.column.name}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
+12
-14
@@ -23,8 +23,10 @@ import { isDefined, TextTools } from '@cloudbeaver/core-utils';
|
||||
import { getValue } from '../../helpers';
|
||||
import { ObjectPropertyTableFooter } from '../ObjectPropertyTableFooter';
|
||||
import { CellFormatter } from './CellFormatter';
|
||||
import type { IDataColumn } from './Column';
|
||||
import { ColumnIcon } from './Columns/ColumnIcon/ColumnIcon';
|
||||
import { ColumnSelect } from './Columns/ColumnSelect/ColumnSelect';
|
||||
import { HeaderRenderer } from './HeaderRenderer';
|
||||
import { RowRenderer } from './RowRenderer';
|
||||
import baseStyles from './styles/base.scss';
|
||||
import { tableStyles } from './styles/styles';
|
||||
@@ -58,21 +60,15 @@ interface Props {
|
||||
}
|
||||
|
||||
function getMeasuredCells(columns: ObjectPropertyInfo[], rows: DBObject[]) {
|
||||
const columnNames = columns.map(column => column.displayName).filter(isDefined);
|
||||
|
||||
let rowStrings: string[] = [];
|
||||
const columnNames = columns.map(column => column.displayName?.toUpperCase()).filter(isDefined);
|
||||
const rowStrings: string[] = Array(columns.length).fill('');
|
||||
|
||||
for (const row of rows.slice(0, 100)) {
|
||||
if (row.object?.properties) {
|
||||
if (rowStrings.length === 0) {
|
||||
rowStrings = row.object.properties.map(p => getValue(p.value));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let i = 0; i < row.object.properties.length; i++) {
|
||||
const value = getValue(row.object.properties[i].value);
|
||||
|
||||
if (value.length > (rowStrings[i] ?? '').length) {
|
||||
if (value.length > rowStrings[i].length) {
|
||||
rowStrings[i] = value;
|
||||
}
|
||||
}
|
||||
@@ -82,12 +78,12 @@ function getMeasuredCells(columns: ObjectPropertyInfo[], rows: DBObject[]) {
|
||||
return TextTools.getWidth({
|
||||
font: '400 12px Roboto',
|
||||
text: columnNames.map((cell, i) => {
|
||||
if (cell.length > (rowStrings[i] ?? '').length) {
|
||||
if (cell.length >= rowStrings[i].length) {
|
||||
return cell;
|
||||
}
|
||||
return rowStrings[i];
|
||||
}),
|
||||
}).map(v => v + 32 + 8);
|
||||
}).map(v => v + 16 + 8);
|
||||
}
|
||||
|
||||
const CUSTOM_COLUMNS = [ColumnSelect, ColumnIcon];
|
||||
@@ -107,20 +103,22 @@ export const Table = observer<Props>(function Table({
|
||||
|
||||
const baseObject = objects
|
||||
.slice()
|
||||
.sort((a, b) => (a.object?.properties?.length || 0) - (b.object?.properties?.length || 0));
|
||||
.sort((a, b) => (b.object?.properties?.length || 0) - (a.object?.properties?.length || 0));
|
||||
|
||||
const nodeIds = objects.map(object => object.id);
|
||||
const properties = baseObject[0].object?.properties || [];
|
||||
const properties = baseObject[0].object?.properties ?? [];
|
||||
const measuredCells = getMeasuredCells(properties, objects);
|
||||
|
||||
const dataColumns = properties.map((property, index) => ({
|
||||
const dataColumns: IDataColumn[] = properties.map((property, index) => ({
|
||||
key: property.id!,
|
||||
name: property.displayName ?? '',
|
||||
description: property.description,
|
||||
columnDataIndex: null,
|
||||
width: Math.min(300, measuredCells[index]),
|
||||
minWidth: 40,
|
||||
resizable: true,
|
||||
formatter: CellFormatter,
|
||||
headerRenderer: HeaderRenderer,
|
||||
}));
|
||||
|
||||
const tableData = useTableData(dataColumns, CUSTOM_COLUMNS);
|
||||
|
||||
+1
@@ -30,6 +30,7 @@
|
||||
border-color: transparent;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.rdg-cell-selected {
|
||||
|
||||
+3
-3
@@ -12,17 +12,17 @@ import type { CalculatedColumn as GridCalculatedColumn } from 'react-data-grid';
|
||||
import type { DBObject } from '@cloudbeaver/core-app';
|
||||
import { useObservableRef } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import type { DataColumn, ICustomColumn } from './Column';
|
||||
import type { IDataColumn, ICustomColumn } from './Column';
|
||||
|
||||
type CalculatedColumn = GridCalculatedColumn<DBObject>;
|
||||
|
||||
export interface ITableData {
|
||||
columns: DataColumn[];
|
||||
columns: IDataColumn[];
|
||||
isCustomColumn: (column: CalculatedColumn) => boolean;
|
||||
getColumnIdx: (column: CalculatedColumn) => number;
|
||||
}
|
||||
|
||||
export function useTableData(dataColumns: DataColumn[], customColumns: ICustomColumn[]): ITableData {
|
||||
export function useTableData(dataColumns: IDataColumn[], customColumns: ICustomColumn[]): ITableData {
|
||||
return useObservableRef(() => ({
|
||||
get columns() {
|
||||
return this.customColumns.slice().sort((a, b) => (a.order ?? 0) - (b.order ?? 0)).concat(this.dataColumns);
|
||||
|
||||
@@ -8,11 +8,21 @@
|
||||
|
||||
import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk';
|
||||
|
||||
export function getValue(value: string | { displayName: string }): string {
|
||||
type Value = string | null | undefined | { displayName: string } | string[];
|
||||
|
||||
export function getValue(value: Value): string {
|
||||
if (value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
return typeof value === 'string' ? value : value.displayName;
|
||||
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
return value;
|
||||
case 'object':
|
||||
return Array.isArray(value) ? value.join(', ') : value.displayName;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function matchType(type?: string) {
|
||||
|
||||
Reference in New Issue
Block a user