Merge branch 'devel' of https://github.com/dbeaver/cloudbeaver into devel

This commit is contained in:
Wroud
2022-04-27 16:52:19 +03:00
24 changed files with 179 additions and 104 deletions
@@ -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,
@@ -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(
@@ -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;
@@ -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");
@@ -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();
}
@@ -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;
@@ -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()});
}
@@ -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;
@@ -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);
}
}
@@ -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,
@@ -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);
}
}
@@ -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);
}
}
@@ -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;
}
@@ -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);
}
@@ -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;
}
}
@@ -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
@@ -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());
@@ -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;
}
@@ -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>
);
});
@@ -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);
@@ -30,6 +30,7 @@
border-color: transparent;
display: flex;
flex-direction: row;
user-select: text;
}
.rdg-cell-selected {
@@ -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) {