diff --git a/samples/sample-databases/cloudbeaver-sample.json b/samples/sample-databases/cloudbeaver-sample.json
deleted file mode 100644
index d642dd54f8..0000000000
--- a/samples/sample-databases/cloudbeaver-sample.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- // Server configuration
- "logView": {
- "logRefreshInterval": 3,
- "logBatchSize": 1000
- }
-}
\ No newline at end of file
diff --git a/server/bundles/io.cloudbeaver.server/db/cb-schema-create.sql b/server/bundles/io.cloudbeaver.server/db/cb-schema-create.sql
index fac0204dc8..5db0ee1f37 100644
--- a/server/bundles/io.cloudbeaver.server/db/cb-schema-create.sql
+++ b/server/bundles/io.cloudbeaver.server/db/cb-schema-create.sql
@@ -9,7 +9,7 @@ CREATE TABLE CB_SERVER(
);
CREATE TABLE CB_USER(
- USER_ID VARCHAR(32) NOT NULL,
+ USER_ID VARCHAR(64) NOT NULL,
IS_ACTIVE CHAR(1) NOT NULL,
CREATE_TIME TIMESTAMP NOT NULL,
@@ -17,6 +17,16 @@ CREATE TABLE CB_USER(
PRIMARY KEY(USER_ID)
);
+-- Additional user properties (profile)
+CREATE TABLE CB_USER_META(
+ USER_ID VARCHAR(64) NOT NULL,
+ META_ID VARCHAR(32) NOT NULL,
+ META_VALUE VARCHAR(1024),
+
+ PRIMARY KEY(USER_ID,META_ID),
+ FOREIGN KEY(USER_ID) REFERENCES CB_USER(USER_ID) ON DELETE CASCADE
+);
+
CREATE TABLE CB_ROLE(
ROLE_ID VARCHAR(32) NOT NULL,
ROLE_NAME VARCHAR(100) NOT NULL,
@@ -39,7 +49,7 @@ CREATE TABLE CB_ROLE_PERMISSIONS(
);
CREATE TABLE CB_USER_ROLE(
- USER_ID VARCHAR(32) NOT NULL,
+ USER_ID VARCHAR(64) NOT NULL,
ROLE_ID VARCHAR(32) NOT NULL,
GRANT_TIME TIMESTAMP NOT NULL,
@@ -50,8 +60,24 @@ CREATE TABLE CB_USER_ROLE(
FOREIGN KEY(ROLE_ID) REFERENCES CB_ROLE(ROLE_ID) ON DELETE CASCADE
);
+CREATE TABLE CB_AUTH_PROVIDER(
+ PROVIDER_ID VARCHAR(32) NOT NULL,
+ IS_ENABLED CHAR(1) NOT NULL,
+
+ PRIMARY KEY(PROVIDER_ID)
+);
+
+CREATE TABLE CB_AUTH_CONFIGURATION(
+ PROVIDER_ID VARCHAR(32) NOT NULL,
+ PARAM_ID VARCHAR(32) NOT NULL,
+ PARAM_VALUE VARCHAR(1024),
+
+ PRIMARY KEY(PROVIDER_ID,PARAM_ID),
+ FOREIGN KEY(PROVIDER_ID) REFERENCES CB_AUTH_PROVIDER(PROVIDER_ID) ON DELETE CASCADE
+);
+
CREATE TABLE CB_USER_CREDENTIALS(
- USER_ID VARCHAR(32) NOT NULL,
+ USER_ID VARCHAR(64) NOT NULL,
PROVIDER_ID VARCHAR(32) NOT NULL,
CRED_ID VARCHAR(32) NOT NULL,
CRED_VALUE VARCHAR(1024) NOT NULL,
@@ -63,7 +89,7 @@ CREATE TABLE CB_USER_CREDENTIALS(
CREATE INDEX CB_USER_CREDENTIALS_SEARCH_IDX ON CB_USER_CREDENTIALS(PROVIDER_ID,CRED_ID);
CREATE TABLE CB_USER_STATE(
- USER_ID VARCHAR(32) NOT NULL,
+ USER_ID VARCHAR(64) NOT NULL,
USER_CONFIGURATION TEXT NULL,
@@ -75,7 +101,7 @@ CREATE TABLE CB_USER_STATE(
CREATE TABLE CB_SESSION(
SESSION_ID VARCHAR(64) NOT NULL,
- USER_ID VARCHAR(32) NULL,
+ USER_ID VARCHAR(64) NULL,
CREATE_TIME TIMESTAMP NOT NULL,
LAST_ACCESS_TIME TIMESTAMP NOT NULL,
@@ -105,3 +131,4 @@ CREATE TABLE CB_SESSION_LOG(
);
CREATE INDEX CB_SESSION_LOG_INDEX ON CB_SESSION_LOG(SESSION_ID,LOG_TIME);
+
diff --git a/server/bundles/io.cloudbeaver.server/plugin.xml b/server/bundles/io.cloudbeaver.server/plugin.xml
index b6b64d80a9..f9651164c4 100644
--- a/server/bundles/io.cloudbeaver.server/plugin.xml
+++ b/server/bundles/io.cloudbeaver.server/plugin.xml
@@ -50,10 +50,12 @@
-
-
-
-
+
+
+
+
+
+
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProviderExternal.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProviderExternal.java
index 9c862e2f47..bb9536de28 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProviderExternal.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProviderExternal.java
@@ -16,6 +16,7 @@
*/
package io.cloudbeaver;
+import io.cloudbeaver.model.user.WebUser;
import org.jkiss.dbeaver.DBException;
import java.util.Map;
@@ -25,9 +26,14 @@ import java.util.Map;
*/
public interface DBWAuthProviderExternal extends DBWAuthProvider {
+ /**
+ * Returns new identifying credentials which can be used to find/create user in database
+ */
Map readExternalCredentials(
Map providerConfig, // Auth provider configuration (e.g. 3rd party auth server address)
Map authParameters // Passed auth parameters (e.g. user name or password)
) throws DBException;
+ WebUser registerNewUser(DBWSecurityController securityController, Map providerConfig, Map credentials) throws DBException;
+
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWSecurityController.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWSecurityController.java
index 8240e93406..9e28237001 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWSecurityController.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWSecurityController.java
@@ -22,6 +22,7 @@ import io.cloudbeaver.model.user.WebUser;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.server.CBPlatform;
import org.jkiss.code.NotNull;
+import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.exec.DBCException;
import java.util.Map;
@@ -41,6 +42,8 @@ public interface DBWSecurityController {
void setUserRoles(String userId, String[] roleIds, String grantorId) throws DBCException;
+ WebUser getUserById(String userId) throws DBCException;
+
///////////////////////////////////////////
// Credentials
@@ -53,7 +56,7 @@ public interface DBWSecurityController {
* Find user with matching credentials.
* It doesn't check credentials like passwords, just searches user id by identifying credentials.
*/
- @NotNull
+ @Nullable
String getUserByCredentials(WebAuthProviderDescriptor authProvider, Map authParameters) throws DBCException;
/**
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java
index 53b57af74f..415bd76809 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java
@@ -39,19 +39,32 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
private ObjectType implType;
private DBWAuthProvider instance;
- private final Map properties = new LinkedHashMap<>();
+ private final Map configurationParameters = new LinkedHashMap<>();
+ private final Map credentialParameters = new LinkedHashMap<>();
public WebAuthProviderDescriptor(IConfigurationElement cfg) {
super(cfg);
this.cfg = cfg;
this.implType = new ObjectType(cfg, "class");
- for (IConfigurationElement propGroup : ArrayUtils.safeArray(cfg.getChildren(PropertyDescriptor.TAG_PROPERTY_GROUP))) {
- String category = propGroup.getAttribute(PropertyDescriptor.ATTR_LABEL);
- IConfigurationElement[] propElements = propGroup.getChildren(PropertyDescriptor.TAG_PROPERTY);
- for (IConfigurationElement prop : propElements) {
- WebAuthProviderPropertyDescriptor propertyDescriptor = new WebAuthProviderPropertyDescriptor(category, prop);
- properties.put(CommonUtils.toString(propertyDescriptor.getId()), propertyDescriptor);
+ for (IConfigurationElement cfgElement : cfg.getChildren("configuration")) {
+ for (IConfigurationElement propGroup : ArrayUtils.safeArray(cfgElement.getChildren(PropertyDescriptor.TAG_PROPERTY_GROUP))) {
+ String category = propGroup.getAttribute(PropertyDescriptor.ATTR_LABEL);
+ IConfigurationElement[] propElements = propGroup.getChildren(PropertyDescriptor.TAG_PROPERTY);
+ for (IConfigurationElement prop : propElements) {
+ PropertyDescriptor propertyDescriptor = new PropertyDescriptor(category, prop);
+ configurationParameters.put(CommonUtils.toString(propertyDescriptor.getId()), propertyDescriptor);
+ }
+ }
+ }
+ for (IConfigurationElement credElement : cfg.getChildren("credentials")) {
+ for (IConfigurationElement propGroup : ArrayUtils.safeArray(credElement.getChildren(PropertyDescriptor.TAG_PROPERTY_GROUP))) {
+ String category = propGroup.getAttribute(PropertyDescriptor.ATTR_LABEL);
+ IConfigurationElement[] propElements = propGroup.getChildren(PropertyDescriptor.TAG_PROPERTY);
+ for (IConfigurationElement prop : propElements) {
+ WebAuthProviderPropertyDescriptor propertyDescriptor = new WebAuthProviderPropertyDescriptor(category, prop);
+ credentialParameters.put(CommonUtils.toString(propertyDescriptor.getId()), propertyDescriptor);
+ }
}
}
}
@@ -73,12 +86,16 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
return cfg.getAttribute("icon");
}
- public List getProperties() {
- return new ArrayList<>(properties.values());
+ public List getConfigurationParameters() {
+ return new ArrayList<>(configurationParameters.values());
}
- public WebAuthProviderPropertyDescriptor getProperty(String id) {
- return properties.get(id);
+ public List getCredentialParameters() {
+ return new ArrayList<>(credentialParameters.values());
+ }
+
+ public WebAuthProviderPropertyDescriptor getCredentialParameter(String id) {
+ return credentialParameters.get(id);
}
@NotNull
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBAppConfig.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBAppConfig.java
index 85a54230a8..471f39d1ca 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBAppConfig.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBAppConfig.java
@@ -23,6 +23,7 @@ public class CBAppConfig {
private boolean anonymousAccessEnabled = true;
private boolean authenticationEnabled = true;
private String anonymousUserRole = CBConstants.DEFAUL_APP_ANONYMOUS_ROLE_NAME;
+ private String defaultUserRole = CBConstants.DEFAUL_APP_ANONYMOUS_ROLE_NAME;
public boolean isAuthenticationEnabled() {
return authenticationEnabled;
@@ -36,4 +37,7 @@ public class CBAppConfig {
return anonymousUserRole;
}
+ public String getDefaultUserRole() {
+ return defaultUserRole;
+ }
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java
index 4fb5dfdb64..9f168742d4 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java
@@ -105,6 +105,10 @@ public class CBApplication extends BaseApplicationImpl {
return maxSessionIdleTime;
}
+ public CBDatabaseConfig getDatabaseConfiguration() {
+ return databaseConfiguration;
+ }
+
public CBAppConfig getAppConfiguration() {
return appConfiguration;
}
diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBSecurityController.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBSecurityController.java
index 61f1195db8..56d7d45088 100644
--- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBSecurityController.java
+++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBSecurityController.java
@@ -17,14 +17,13 @@
package io.cloudbeaver.server;
import io.cloudbeaver.DBWSecurityController;
-import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.user.WebRole;
import io.cloudbeaver.model.user.WebUser;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderPropertyDescriptor;
import io.cloudbeaver.registry.WebAuthProviderPropertyEncryption;
-import org.jkiss.code.NotNull;
+import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils;
@@ -97,6 +96,23 @@ class CBSecurityController implements DBWSecurityController {
}
}
+ @Override
+ public WebUser getUserById(String userId) throws DBCException {
+ try (Connection dbCon = database.openConnection()) {
+ try (PreparedStatement dbStat = dbCon.prepareStatement("SELECT * FROM CB_USER WHERE USER_ID=?")) {
+ dbStat.setString(1, userId);
+ try (ResultSet dbResult = dbStat.executeQuery()) {
+ if (dbResult.next()) {
+ return new WebUser(dbResult.getString(1));
+ }
+ return null;
+ }
+ }
+ } catch (SQLException e) {
+ throw new DBCException("Error while searching credentials", e);
+ }
+ }
+
///////////////////////////////////////////
// Credentials
@@ -106,7 +122,7 @@ class CBSecurityController implements DBWSecurityController {
try {
transformedCredentials = credentials.entrySet().stream().map(cred -> {
String propertyName = cred.getKey();
- WebAuthProviderPropertyDescriptor property = authProvider.getProperty(propertyName);
+ WebAuthProviderPropertyDescriptor property = authProvider.getCredentialParameter(propertyName);
if (property == null) {
throw new IllegalArgumentException("Invalid auth provider '" + authProvider.getId() + "' property '" + propertyName + "'");
}
@@ -115,7 +131,7 @@ class CBSecurityController implements DBWSecurityController {
return new String[] {propertyName, encodedValue };
}).collect(Collectors.toList());
} catch (Exception e) {
- throw new DBCException("Error passing properties to provider", e);
+ throw new DBCException(e.getMessage(), e);
}
try (Connection dbCon = database.openConnection()) {
JDBCUtils.executeStatement(dbCon, "DELETE FROM CB_USER_CREDENTIALS WHERE USER_ID=? AND PROVIDER_ID=?", userId, authProvider.getId());
@@ -135,11 +151,11 @@ class CBSecurityController implements DBWSecurityController {
}
}
- @NotNull
+ @Nullable
@Override
public String getUserByCredentials(WebAuthProviderDescriptor authProvider, Map authParameters) throws DBCException {
Map identCredentials = new LinkedHashMap<>();
- for (WebAuthProviderPropertyDescriptor prop : authProvider.getProperties()) {
+ for (WebAuthProviderPropertyDescriptor prop : authProvider.getCredentialParameters()) {
if (prop.isIdentifying()) {
String propId = CommonUtils.toString(prop.getId());
Object paramValue = authParameters.get(propId);
@@ -191,11 +207,7 @@ class CBSecurityController implements DBWSecurityController {
}
}
- if (userId == null) {
- // User doesn't exist. We can create new user automatically if auth provider supports this
- throw new DBCException("Invalid user credentials");
- }
- if (!isActive) {
+ if (userId != null && !isActive) {
throw new DBCException("User account is locked");
}
diff --git a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls
index ad1edba81e..b74d4185f9 100644
--- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls
+++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls
@@ -25,7 +25,8 @@ type AuthProviderInfo {
isDefault: Boolean
- properties: [AuthCredentialInfo]!
+ configurationParameters: [ObjectPropertyInfo]!
+ credentialParameters: [AuthCredentialInfo]!
}
type UserAuthInfo {
diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java
index 2f9cdee10e..b38730e8d9 100644
--- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java
+++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java
@@ -28,6 +28,7 @@ import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.service.auth.DBWServiceAuth;
import io.cloudbeaver.service.auth.WebAuthInfo;
import org.jkiss.dbeaver.DBException;
+import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.utils.CommonUtils;
import java.time.OffsetDateTime;
@@ -53,15 +54,26 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
Map providerConfig = Collections.emptyMap();
DBWAuthProvider> authProviderInstance = authProvider.getInstance();
- if (authProviderInstance instanceof DBWAuthProviderExternal>) {
- Map externalCredentials = ((DBWAuthProviderExternal>) authProviderInstance).readExternalCredentials(providerConfig,
- authParameters);
- if (externalCredentials != null) {
- authParameters.putAll(externalCredentials);
+ DBWAuthProviderExternal> authProviderExternal = authProviderInstance instanceof DBWAuthProviderExternal> ?
+ (DBWAuthProviderExternal>) authProviderInstance : null;
+ if (authProviderExternal != null) {
+ authParameters = authProviderExternal.readExternalCredentials(providerConfig, authParameters);
+ }
+
+ WebUser user = null;
+ String userId = serverController.getUserByCredentials(authProvider, authParameters);
+ if (userId == null) {
+ // User doesn't exist. We can create new user automatically if auth provider supports this
+ if (authProviderExternal != null) {
+ user = authProviderExternal.registerNewUser(serverController, providerConfig, authParameters);
+ userId = user.getUserId();
+ }
+
+ if (userId == null) {
+ throw new DBCException("Invalid user credentials");
}
}
- String userId = serverController.getUserByCredentials(authProvider, authParameters);
Map userCredentials = serverController.getUserCredentials(userId, authProvider);
Object authToken = authProviderInstance.openSession(
@@ -75,7 +87,9 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
authInfo.setAuthToken(authToken);
authInfo.setMessage("Logged using " + authProvider.getLabel() + " provider");
- WebUser user = new WebUser(userId);
+ if (user == null) {
+ user = new WebUser(userId);
+ }
webSession.setUser(user);
return authInfo;