From c2ee15380e8a0306712740e425be1bd01628f778 Mon Sep 17 00:00:00 2001 From: serge-rider Date: Fri, 1 May 2020 15:55:49 +0300 Subject: [PATCH] #8 Authentication implementation --- .../META-INF/MANIFEST.MF | 1 + .../db/cb-schema-create.sql | 12 +-- .../bundles/io.cloudbeaver.server/plugin.xml | 2 +- .../src/io/cloudbeaver/DBWAuthProvider.java | 1 - .../io/cloudbeaver/DBWServerController.java | 17 ++- .../src/io/cloudbeaver/DBWebException.java | 12 ++- .../provider/local/LocalAuthProvider.java | 6 +- .../io/cloudbeaver/model/WebServerConfig.java | 4 +- .../cloudbeaver/model/WebServiceConfig.java | 2 +- .../model/user/WebAuthProviderInfo.java | 2 +- .../cloudbeaver/model/user/WebPermission.java | 2 +- .../registry/WebAuthProviderDescriptor.java | 5 +- .../WebAuthProviderPropertyDescriptor.java | 8 +- .../WebAuthProviderPropertyEncryption.java | 2 +- .../registry/WebDriverRegistry.java | 2 +- .../registry/WebPermissionDescriptor.java | 2 +- .../registry/WebServiceDescriptor.java | 2 +- .../registry/WebServiceRegistry.java | 2 +- .../src/io/cloudbeaver/server/CBDatabase.java | 11 +- .../src/io/cloudbeaver/server/CBPlatform.java | 2 +- .../server/CBServerController.java | 102 ++++++++++++++++-- .../server/graphql/GraphQLBindingContext.java | 6 +- .../server/graphql/GraphQLEndpoint.java | 2 +- .../server/jetty/CBJettyServer.java | 2 +- .../schema/service.auth.graphqls | 6 +- .../service/auth/DBWServiceAuth.java | 2 +- .../cloudbeaver/service/auth/WebAuthInfo.java | 36 +++++-- .../service/auth/WebServiceBindingAuth.java | 13 +-- .../service/auth/impl/WebServiceAuthImpl.java | 43 +++++++- 29 files changed, 233 insertions(+), 76 deletions(-) rename server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/{server => }/registry/WebAuthProviderDescriptor.java (95%) rename server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/{server => }/registry/WebAuthProviderPropertyDescriptor.java (85%) rename server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/{server => }/registry/WebAuthProviderPropertyEncryption.java (97%) rename server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/{server => }/registry/WebDriverRegistry.java (98%) rename server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/{server => }/registry/WebPermissionDescriptor.java (97%) rename server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/{server => }/registry/WebServiceDescriptor.java (98%) rename server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/{server => }/registry/WebServiceRegistry.java (99%) diff --git a/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF index 49e3178fe8..9a5b1b5726 100644 --- a/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF @@ -30,6 +30,7 @@ Export-Package: io.cloudbeaver, io.cloudbeaver.model, io.cloudbeaver.model.resources, io.cloudbeaver.model.session, + io.cloudbeaver.registry, io.cloudbeaver.service, io.cloudbeaver.service.navigator, io.cloudbeaver.service.sql 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 95e1495a33..380136890e 100644 --- a/server/bundles/io.cloudbeaver.server/db/cb-schema-create.sql +++ b/server/bundles/io.cloudbeaver.server/db/cb-schema-create.sql @@ -60,6 +60,8 @@ CREATE TABLE CB_USER_CREDENTIALS( FOREIGN KEY(USER_ID) REFERENCES CB_USER(USER_ID) ON DELETE CASCADE ); +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, @@ -92,16 +94,6 @@ CREATE TABLE CB_SESSION_STATE( FOREIGN KEY(SESSION_ID) REFERENCES CB_SESSION(SESSION_ID) ON DELETE CASCADE ); -CREATE TABLE CB_SESSION_STATE( - SESSION_ID VARCHAR(64) NOT NULL, - - SESSION_STATE TEXT NOT NULL, - UPDATE_TIME TIMESTAMP NOT NULL, - - PRIMARY KEY(SESSION_ID), - FOREIGN KEY(SESSION_ID) REFERENCES CB_SESSION(SESSION_ID) ON DELETE CASCADE -); - CREATE TABLE CB_SESSION_LOG( SESSION_ID VARCHAR(64) NOT NULL, diff --git a/server/bundles/io.cloudbeaver.server/plugin.xml b/server/bundles/io.cloudbeaver.server/plugin.xml index bedccc98db..1832de9001 100644 --- a/server/bundles/io.cloudbeaver.server/plugin.xml +++ b/server/bundles/io.cloudbeaver.server/plugin.xml @@ -48,7 +48,7 @@ - + diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProvider.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProvider.java index fc5147c9c8..0a1f0e9ffc 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProvider.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProvider.java @@ -26,7 +26,6 @@ import java.util.Map; public interface DBWAuthProvider { AUTH_SESSION openSession( - String userName, Map providerConfig, // Auth provider configuration (e.g. 3rd party auth server address) Map userCredentials, // Saved user credentials (e.g. associated 3rd party provider user name or realm) Map authParameters // Passed auth parameters (e.g. user name or password) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWServerController.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWServerController.java index a25b5e9e6c..a7ba88216f 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWServerController.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWServerController.java @@ -18,6 +18,7 @@ package io.cloudbeaver; import io.cloudbeaver.model.user.WebRole; import io.cloudbeaver.model.user.WebUser; +import io.cloudbeaver.registry.WebAuthProviderDescriptor; import org.jkiss.dbeaver.model.exec.DBCException; import java.util.Map; @@ -33,7 +34,21 @@ public interface DBWServerController { void setUserRoles(String userId, String[] roleIds, String grantorId) throws DBCException; - void setUserCredentials(String userId, String authProviderId, Map credentials) throws DBCException; + /** + * Sets user redentials for specified provider + */ + void setUserCredentials(String userId, WebAuthProviderDescriptor authProvider, Map credentials) throws DBCException; + + /** + * Find user with matching credentials. + * It doesn't check credentials like passwords, just searches user id by identifying credentials. + */ + String findUserByCredentials(WebAuthProviderDescriptor authProvider, Map authParameters) throws DBCException; + + /** + * Get user credentials for specified provider + */ + Map getUserCredentials(String userId, WebAuthProviderDescriptor authProvider) throws DBCException; WebRole[] readAllRoles() throws DBCException; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebException.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebException.java index 9fead31ae5..5335f240ae 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebException.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebException.java @@ -21,6 +21,7 @@ import graphql.ErrorType; import graphql.GraphQLError; import graphql.language.SourceLocation; import io.cloudbeaver.server.graphql.GraphQLEndpoint; +import io.cloudbeaver.service.WebServiceBindingBase; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.DBPDataSource; import org.jkiss.dbeaver.utils.GeneralUtils; @@ -98,7 +99,10 @@ public class DBWebException extends DBException implements GraphQLError { Map extensions = new LinkedHashMap<>(); String stString = buf.toString(); - int divPos = stString.indexOf(GraphQLEndpoint.class.getName()); + int divPos = stString.indexOf(WebServiceBindingBase.class.getName()); + if (divPos == -1) { + divPos = stString.indexOf(GraphQLEndpoint.class.getName()); + } if (divPos != -1) { stString = stString.substring(0, divPos); divPos = stString.lastIndexOf(")"); @@ -106,6 +110,12 @@ public class DBWebException extends DBException implements GraphQLError { stString = stString.substring(0, divPos + 1); } } + divPos = stString.indexOf(':'); + if (divPos != -1) { + String exceptionClass = stString.substring(0, divPos); + extensions.put("exceptionClass", exceptionClass); + stString = stString.substring(divPos + 1).trim(); + } extensions.put("stackTrace", stString); int errorCode = getErrorCode(); if (errorCode != ERROR_CODE_NONE) { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/auth/provider/local/LocalAuthProvider.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/auth/provider/local/LocalAuthProvider.java index 7112a2186d..2b0974bfed 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/auth/provider/local/LocalAuthProvider.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/auth/provider/local/LocalAuthProvider.java @@ -17,7 +17,7 @@ package io.cloudbeaver.auth.provider.local; import io.cloudbeaver.DBWAuthProvider; -import io.cloudbeaver.server.registry.WebAuthProviderPropertyEncryption; +import io.cloudbeaver.registry.WebAuthProviderPropertyEncryption; import org.jkiss.dbeaver.DBException; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.SecurityUtils; @@ -30,10 +30,12 @@ import java.util.Map; public class LocalAuthProvider implements DBWAuthProvider { public static final String PROVIDER_ID = "local"; + public static final String CRED_USER = "user"; public static final String CRED_PASSWORD = "password"; @Override - public LocalAuthToken openSession(String userName, Map providerConfig, Map userCredentials, Map authParameters) throws DBException { + public LocalAuthToken openSession(Map providerConfig, Map userCredentials, Map authParameters) throws DBException { + String userName = CommonUtils.toString(authParameters.get(CRED_USER), null); String storedPasswordHash = CommonUtils.toString(userCredentials.get(CRED_PASSWORD), null); if (CommonUtils.isEmpty(storedPasswordHash)) { throw new DBException("User has no password (login restricted)"); diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java index bf360fe731..bc649cae28 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java @@ -18,8 +18,8 @@ package io.cloudbeaver.model; import io.cloudbeaver.WebAction; import io.cloudbeaver.server.CBPlatform; -import io.cloudbeaver.server.registry.WebServiceDescriptor; -import io.cloudbeaver.server.registry.WebServiceRegistry; +import io.cloudbeaver.registry.WebServiceDescriptor; +import io.cloudbeaver.registry.WebServiceRegistry; import org.jkiss.dbeaver.model.meta.Property; import org.jkiss.dbeaver.registry.language.PlatformLanguageDescriptor; import org.jkiss.dbeaver.registry.language.PlatformLanguageRegistry; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServiceConfig.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServiceConfig.java index 7aaa77787a..c1b0c875e9 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServiceConfig.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServiceConfig.java @@ -16,7 +16,7 @@ */ package io.cloudbeaver.model; -import io.cloudbeaver.server.registry.WebServiceDescriptor; +import io.cloudbeaver.registry.WebServiceDescriptor; import org.jkiss.dbeaver.model.meta.Property; /** diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java index 850024f24d..306b8c6f26 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java @@ -16,7 +16,7 @@ */ package io.cloudbeaver.model.user; -import io.cloudbeaver.server.registry.WebAuthProviderDescriptor; +import io.cloudbeaver.registry.WebAuthProviderDescriptor; import org.jkiss.dbeaver.Log; /** diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebPermission.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebPermission.java index 70ad1113a3..e925dc7efc 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebPermission.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebPermission.java @@ -16,7 +16,7 @@ */ package io.cloudbeaver.model.user; -import io.cloudbeaver.server.registry.WebPermissionDescriptor; +import io.cloudbeaver.registry.WebPermissionDescriptor; import org.jkiss.dbeaver.Log; /** diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebAuthProviderDescriptor.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java similarity index 95% rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebAuthProviderDescriptor.java rename to server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java index 79cdb59a8e..53b57af74f 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebAuthProviderDescriptor.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.cloudbeaver.server.registry; +package io.cloudbeaver.registry; import io.cloudbeaver.DBWAuthProvider; import org.eclipse.core.runtime.IConfigurationElement; @@ -22,7 +22,6 @@ import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.impl.AbstractDescriptor; import org.jkiss.dbeaver.model.impl.PropertyDescriptor; -import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor; import org.jkiss.utils.ArrayUtils; import org.jkiss.utils.CommonUtils; @@ -74,7 +73,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor { return cfg.getAttribute("icon"); } - public List getProperties() { + public List getProperties() { return new ArrayList<>(properties.values()); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebAuthProviderPropertyDescriptor.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderPropertyDescriptor.java similarity index 85% rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebAuthProviderPropertyDescriptor.java rename to server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderPropertyDescriptor.java index f3109bb66b..11ed3b9745 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebAuthProviderPropertyDescriptor.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderPropertyDescriptor.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.cloudbeaver.server.registry; +package io.cloudbeaver.registry; import org.eclipse.core.runtime.IConfigurationElement; import org.jkiss.dbeaver.model.impl.PropertyDescriptor; @@ -27,6 +27,7 @@ import org.jkiss.utils.CommonUtils; public class WebAuthProviderPropertyDescriptor extends PropertyDescriptor { private WebAuthProviderPropertyEncryption encryption; + private boolean identifying; // Identifying parameter. Will be used during auth for user search by credentials private boolean admin; // Parameter value can be configured in admin panel private boolean user; // Parameter can be passed by end-user from UI @@ -34,6 +35,7 @@ public class WebAuthProviderPropertyDescriptor extends PropertyDescriptor { super(category, config); this.encryption = CommonUtils.valueOf(WebAuthProviderPropertyEncryption.class, config.getAttribute("encryption"), WebAuthProviderPropertyEncryption.none); + this.identifying = CommonUtils.getBoolean(config.getAttribute("identifying"), false); this.admin = CommonUtils.getBoolean(config.getAttribute("admin"), false); this.user = CommonUtils.getBoolean(config.getAttribute("user"), false); } @@ -42,6 +44,10 @@ public class WebAuthProviderPropertyDescriptor extends PropertyDescriptor { return encryption; } + public boolean isIdentifying() { + return identifying; + } + public boolean isAdmin() { return admin; } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebAuthProviderPropertyEncryption.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderPropertyEncryption.java similarity index 97% rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebAuthProviderPropertyEncryption.java rename to server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderPropertyEncryption.java index 3bde200f8a..e743253ac0 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebAuthProviderPropertyEncryption.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebAuthProviderPropertyEncryption.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.cloudbeaver.server.registry; +package io.cloudbeaver.registry; import org.jkiss.utils.SecurityUtils; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebDriverRegistry.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebDriverRegistry.java similarity index 98% rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebDriverRegistry.java rename to server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebDriverRegistry.java index 5463ba92da..51954b0711 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebDriverRegistry.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebDriverRegistry.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.cloudbeaver.server.registry; +package io.cloudbeaver.registry; import io.cloudbeaver.WebServiceUtils; import org.eclipse.core.runtime.IConfigurationElement; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebPermissionDescriptor.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebPermissionDescriptor.java similarity index 97% rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebPermissionDescriptor.java rename to server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebPermissionDescriptor.java index 062c74feb7..ae1b3b53cf 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebPermissionDescriptor.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebPermissionDescriptor.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.cloudbeaver.server.registry; +package io.cloudbeaver.registry; import org.eclipse.core.runtime.IConfigurationElement; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebServiceDescriptor.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebServiceDescriptor.java similarity index 98% rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebServiceDescriptor.java rename to server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebServiceDescriptor.java index 3c333cac34..15e8acdb2a 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebServiceDescriptor.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebServiceDescriptor.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package io.cloudbeaver.server.registry; +package io.cloudbeaver.registry; import io.cloudbeaver.service.DBWServiceBinding; import org.eclipse.core.runtime.IConfigurationElement; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebServiceRegistry.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebServiceRegistry.java similarity index 99% rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebServiceRegistry.java rename to server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebServiceRegistry.java index cd1de3f2e3..f02b6fc951 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/registry/WebServiceRegistry.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/registry/WebServiceRegistry.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.cloudbeaver.server.registry; +package io.cloudbeaver.registry; import io.cloudbeaver.service.DBWServiceBinding; import org.eclipse.core.runtime.IConfigurationElement; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBDatabase.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBDatabase.java index 09463bb3a3..a147940157 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBDatabase.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBDatabase.java @@ -20,6 +20,8 @@ import io.cloudbeaver.DBWServerController; import io.cloudbeaver.auth.provider.local.LocalAuthProvider; import io.cloudbeaver.model.user.WebRole; import io.cloudbeaver.model.user.WebUser; +import io.cloudbeaver.registry.WebAuthProviderDescriptor; +import io.cloudbeaver.registry.WebServiceRegistry; import org.apache.commons.dbcp2.DriverConnectionFactory; import org.apache.commons.dbcp2.PoolableConnection; import org.apache.commons.dbcp2.PoolableConnectionFactory; @@ -29,7 +31,6 @@ import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBConstants; -import org.jkiss.dbeaver.model.app.DBPPlatform; import org.jkiss.dbeaver.model.exec.DBCException; import org.jkiss.dbeaver.model.runtime.LoggingProgressMonitor; import org.jkiss.dbeaver.registry.DataSourceProviderRegistry; @@ -47,7 +48,6 @@ import java.sql.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; /** * Database management @@ -237,8 +237,13 @@ public class CBDatabase { String clientPassword = LocalAuthProvider.makeClientPasswordHash(adminUser.getUserId(), userPassword); Map credentials = new LinkedHashMap<>(); + credentials.put(LocalAuthProvider.CRED_USER, adminUser.getUserId()); credentials.put(LocalAuthProvider.CRED_PASSWORD, clientPassword); - serverController.setUserCredentials(adminUser.getUserId(), LocalAuthProvider.PROVIDER_ID, credentials); + + WebAuthProviderDescriptor authProvider = WebServiceRegistry.getInstance().getAuthProvider(LocalAuthProvider.PROVIDER_ID); + if (authProvider != null) { + serverController.setUserCredentials(adminUser.getUserId(), authProvider, credentials); + } } if (!CommonUtils.isEmpty(initialData.getRoles())) { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java index 129c72e2fe..aca5bb3489 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBPlatform.java @@ -22,7 +22,7 @@ import io.cloudbeaver.WebServiceUtils; import io.cloudbeaver.model.WebDataSourceConfig; import io.cloudbeaver.model.WebServerConfig; import io.cloudbeaver.model.session.WebSessionManager; -import io.cloudbeaver.server.registry.WebDriverRegistry; +import io.cloudbeaver.registry.WebDriverRegistry; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Platform; import org.jkiss.code.NotNull; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerController.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerController.java index d60df9ee4b..afe6675b34 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerController.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBServerController.java @@ -19,9 +19,10 @@ package io.cloudbeaver.server; import io.cloudbeaver.DBWServerController; import io.cloudbeaver.model.user.WebRole; import io.cloudbeaver.model.user.WebUser; -import io.cloudbeaver.server.registry.WebAuthProviderDescriptor; -import io.cloudbeaver.server.registry.WebAuthProviderPropertyDescriptor; -import io.cloudbeaver.server.registry.WebServiceRegistry; +import io.cloudbeaver.registry.WebAuthProviderDescriptor; +import io.cloudbeaver.registry.WebAuthProviderPropertyDescriptor; +import io.cloudbeaver.registry.WebAuthProviderPropertyEncryption; +import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.exec.DBCException; import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils; import org.jkiss.utils.ArrayUtils; @@ -38,6 +39,8 @@ import java.util.stream.Collectors; */ class CBServerController implements DBWServerController { + private static final Log log = Log.getLog(CBServerController.class); + private final CBDatabase database; CBServerController(CBDatabase database) { @@ -88,18 +91,14 @@ class CBServerController implements DBWServerController { } @Override - public void setUserCredentials(String userId, String authProviderId, Map credentials) throws DBCException { - WebAuthProviderDescriptor authProvider = WebServiceRegistry.getInstance().getAuthProvider(authProviderId); - if (authProvider == null) { - throw new DBCException("Invalid auth provider '" + authProviderId + "'"); - } + public void setUserCredentials(String userId, WebAuthProviderDescriptor authProvider, Map credentials) throws DBCException { List transformedCredentials; try { transformedCredentials = credentials.entrySet().stream().map(cred -> { String propertyName = cred.getKey(); WebAuthProviderPropertyDescriptor property = authProvider.getProperty(propertyName); if (property == null) { - throw new IllegalArgumentException("Invalid auth provider '" + authProviderId + "' property '" + propertyName + "'"); + throw new IllegalArgumentException("Invalid auth provider '" + authProvider.getId() + "' property '" + propertyName + "'"); } String encodedValue = CommonUtils.toString(cred.getValue()); encodedValue = property.getEncryption().encrypt(userId, encodedValue); @@ -109,12 +108,12 @@ class CBServerController implements DBWServerController { throw new DBCException("Error passing properties to provider", e); } try (Connection dbCon = database.openConnection()) { - JDBCUtils.executeStatement(dbCon, "DELETE FROM CB_USER_CREDENTIALS WHERE USER_ID=? AND PROVIDER_ID=?", userId, authProviderId); + JDBCUtils.executeStatement(dbCon, "DELETE FROM CB_USER_CREDENTIALS WHERE USER_ID=? AND PROVIDER_ID=?", userId, authProvider.getId()); if (!CommonUtils.isEmpty(credentials)) { try (PreparedStatement dbStat = dbCon.prepareStatement("INSERT INTO CB_USER_CREDENTIALS(USER_ID,PROVIDER_ID,CRED_ID,CRED_VALUE) VALUES(?,?,?,?)")) { for (String[] cred : transformedCredentials) { dbStat.setString(1, userId); - dbStat.setString(2, authProviderId); + dbStat.setString(2, authProvider.getId()); dbStat.setString(3, cred[0]); dbStat.setString(4, cred[1]); dbStat.execute(); @@ -126,6 +125,87 @@ class CBServerController implements DBWServerController { } } + @Override + public String findUserByCredentials(WebAuthProviderDescriptor authProvider, Map authParameters) throws DBCException { + Map identCredentials = new LinkedHashMap<>(); + for (WebAuthProviderPropertyDescriptor prop : authProvider.getProperties()) { + if (prop.isIdentifying()) { + String propId = CommonUtils.toString(prop.getId()); + Object paramValue = authParameters.get(propId); + if (paramValue == null) { + throw new DBCException("Authentication parameter '" + prop.getId() + "' is missing"); + } + if (prop.getEncryption() == WebAuthProviderPropertyEncryption.hash) { + throw new DBCException("Hash encryption can't be used in identifying credentials"); + } + identCredentials.put(propId, paramValue); + } + } + if (identCredentials.isEmpty()) { + throw new DBCException("No identifying credentials in provider '" + authProvider.getId() + "'"); + } + StringBuilder sql = new StringBuilder(); + sql.append("SELECT UC.USER_ID FROM CB_USER_CREDENTIALS UC\n"); + for (int joinNum = 0; joinNum < identCredentials.size() - 1; joinNum++) { + String joinAlias = "UC" + (joinNum + 2); + sql.append(",CB_USER_CREDENTIALS ").append(joinAlias).append(" ON ") + .append(joinAlias).append(".USER_ID=UC.USER_ID") + .append(joinAlias).append(".PROVIDER_ID=UC.PROVIDER_ID AND ") + .append(joinAlias).append("CRED_ID=? AND ") + .append(joinAlias).append("CRED_VALUE=?"); + } + sql.append("WHERE UC.CRED_ID=? AND UC.CRED_VALUE=? AND UC.PROVIDER_ID=?"); + try (Connection dbCon = database.openConnection()) { + try (PreparedStatement dbStat = dbCon.prepareStatement(sql.toString())) { + int param = 1; + for (Map.Entry credEntry : identCredentials.entrySet()) { + dbStat.setString(param++, credEntry.getKey()); + dbStat.setString(param++, CommonUtils.toString(credEntry.getValue())); + } + dbStat.setString(param, authProvider.getId()); + + try (ResultSet dbResult = dbStat.executeQuery()) { + String userId = null; + while (dbResult.next()) { + String credUserId = dbResult.getString(1); + if (userId == null) { + userId = credUserId; + } else if (!userId.equals(credUserId)) { + log.error("Multiple users associated with the same credentials! " + credUserId + ", " + userId); + } + } + return userId; + } + } + } catch (SQLException e) { + throw new DBCException("Error while searching credentials", e); + } + } + + @Override + public Map getUserCredentials(String userId, WebAuthProviderDescriptor authProvider) throws DBCException { + try (Connection dbCon = database.openConnection()) { + try (PreparedStatement dbStat = dbCon.prepareStatement( + "SELECT CRED_ID,CRED_VALUE FROM CB_USER_CREDENTIALS\n" + + "WHERE USER_ID=? AND PROVIDER_ID=?")) { + dbStat.setString(1, userId); + dbStat.setString(2, authProvider.getId()); + + try (ResultSet dbResult = dbStat.executeQuery()) { + Map credentials = new LinkedHashMap<>(); + + while (dbResult.next()) { + credentials.put(dbResult.getString(1), dbResult.getString(2)); + } + + return credentials; + } + } + } catch (SQLException e) { + throw new DBCException("Error saving role in database", e); + } + } + @Override public WebRole[] readAllRoles() throws DBCException { try (Connection dbCon = database.openConnection()) { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/graphql/GraphQLBindingContext.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/graphql/GraphQLBindingContext.java index c69e4ed1d4..2eed2bd944 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/graphql/GraphQLBindingContext.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/graphql/GraphQLBindingContext.java @@ -1,8 +1,6 @@ package io.cloudbeaver.server.graphql; -import graphql.GraphQLContext; import graphql.scalars.ExtendedScalars; -import graphql.schema.DataFetchingEnvironment; import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.TypeRuntimeWiring; import io.cloudbeaver.DBWebException; @@ -11,8 +9,8 @@ import io.cloudbeaver.service.DBWBindingContext; import io.cloudbeaver.service.DBWServiceBinding; import io.cloudbeaver.service.DBWServiceBindingGraphQL; import io.cloudbeaver.model.session.WebSessionManager; -import io.cloudbeaver.server.registry.WebServiceDescriptor; -import io.cloudbeaver.server.registry.WebServiceRegistry; +import io.cloudbeaver.registry.WebServiceDescriptor; +import io.cloudbeaver.registry.WebServiceRegistry; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.runtime.DBWorkbench; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/graphql/GraphQLEndpoint.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/graphql/GraphQLEndpoint.java index a2e8565758..42fcff9586 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/graphql/GraphQLEndpoint.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/graphql/GraphQLEndpoint.java @@ -34,7 +34,7 @@ import graphql.schema.idl.TypeDefinitionRegistry; import io.cloudbeaver.DBWebException; import io.cloudbeaver.WebServiceUtils; import io.cloudbeaver.server.CBApplication; -import io.cloudbeaver.server.registry.WebServiceRegistry; +import io.cloudbeaver.registry.WebServiceRegistry; import io.cloudbeaver.service.DBWServiceBindingGraphQL; import org.jkiss.dbeaver.Log; import org.jkiss.utils.IOUtils; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java index 608651d1ef..fa0e67c90d 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jetty/CBJettyServer.java @@ -3,7 +3,7 @@ package io.cloudbeaver.server.jetty; import io.cloudbeaver.service.DBWServiceBindingServlet; import io.cloudbeaver.server.CBApplication; import io.cloudbeaver.server.graphql.GraphQLEndpoint; -import io.cloudbeaver.server.registry.WebServiceRegistry; +import io.cloudbeaver.registry.WebServiceRegistry; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConnectionFactory; 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 0a6155d9ce..aa8b878f0e 100644 --- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls +++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls @@ -26,9 +26,9 @@ type AuthProviderInfo { } type UserAuthInfo { - userName: String! - - loginTime: DateTime + userId: String! + authProvider: String! + loginTime: DateTime! # Optional login message message: String diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java index 23b39d619c..aaa25fdbae 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java @@ -27,7 +27,7 @@ import java.util.Map; */ public interface DBWServiceAuth extends DBWService { - String authLogin(WebSession webSession, String providerId, Map credentials) throws DBWebException; + WebAuthInfo authLogin(WebSession webSession, String providerId, Map credentials) throws DBWebException; void authLogout(WebSession webSession) throws DBWebException; diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebAuthInfo.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebAuthInfo.java index c55dcf8dd1..4f2130a875 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebAuthInfo.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebAuthInfo.java @@ -16,6 +16,8 @@ */ package io.cloudbeaver.service.auth; +import java.time.LocalDate; +import java.time.OffsetDateTime; import java.util.Date; /** @@ -23,23 +25,41 @@ import java.util.Date; */ public class WebAuthInfo { - private String userName; - private Date loginTime; + private String userId; + private String authProvider; + private Object authToken; + private OffsetDateTime loginTime; private String message; - public String getUserName() { - return userName; + public String getUserId() { + return userId; } - public void setUserName(String userName) { - this.userName = userName; + public void setUserId(String userId) { + this.userId = userId; } - public Date getLoginTime() { + public String getAuthProvider() { + return authProvider; + } + + public void setAuthProvider(String authProvider) { + this.authProvider = authProvider; + } + + public Object getAuthToken() { + return authToken; + } + + public void setAuthToken(Object authToken) { + this.authToken = authToken; + } + + public OffsetDateTime getLoginTime() { return loginTime; } - public void setLoginTime(Date loginTime) { + public void setLoginTime(OffsetDateTime loginTime) { this.loginTime = loginTime; } diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java index 0962b5d32f..b5a6eb5a7d 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java @@ -17,13 +17,11 @@ package io.cloudbeaver.service.auth; import io.cloudbeaver.DBWebException; -import io.cloudbeaver.server.registry.WebServiceRegistry; +import io.cloudbeaver.registry.WebServiceRegistry; import io.cloudbeaver.service.DBWBindingContext; import io.cloudbeaver.service.WebServiceBindingBase; import io.cloudbeaver.service.auth.impl.WebServiceAuthImpl; -import java.util.Date; - /** * Web service implementation */ @@ -39,15 +37,10 @@ public class WebServiceBindingAuth extends WebServiceBindingBase public void bindWiring(DBWBindingContext model) throws DBWebException { model.getQueryType() .dataFetcher("authLogin", env -> { - String userId = getService(env).authLogin( + return getService(env).authLogin( getWebSession(env), - env.getArgument("providerId"), + env.getArgument("provider"), env.getArgument("credentials")); - WebAuthInfo authInfo = new WebAuthInfo(); - authInfo.setUserName(userId); - authInfo.setLoginTime(new Date()); - authInfo.setMessage("User logged in"); - return authInfo; }) .dataFetcher("authLogout", env -> { getService(env).authLogout(getWebSession(env)); 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 2db9217534..02d8c7cd97 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 @@ -16,10 +16,19 @@ */ package io.cloudbeaver.service.auth.impl; +import io.cloudbeaver.DBWServerController; import io.cloudbeaver.DBWebException; import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.registry.WebAuthProviderDescriptor; +import io.cloudbeaver.registry.WebServiceRegistry; +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.utils.CommonUtils; +import java.time.OffsetDateTime; +import java.util.Collections; import java.util.Map; /** @@ -27,10 +36,38 @@ import java.util.Map; */ public class WebServiceAuthImpl implements DBWServiceAuth { - @Override - public String authLogin(WebSession webSession, String providerId, Map credentials) throws DBWebException { - return null; + public WebAuthInfo authLogin(WebSession webSession, String providerId, Map authParameters) throws DBWebException { + if (CommonUtils.isEmpty(providerId)) { + throw new DBWebException("Missing auth provider parameter"); + } + WebAuthProviderDescriptor authProvider = WebServiceRegistry.getInstance().getAuthProvider(providerId); + if (authProvider == null) { + throw new DBWebException("Invalid auth provider '" + providerId + "'"); + } + DBWServerController serverController = CBPlatform.getInstance().getApplication().getServerController(); + try { + String userId = serverController.findUserByCredentials(authProvider, authParameters); + if (userId == null) { + // User doesn't exist. We can create new user automatically if auth provider supports this + throw new DBWebException("Invalid user credentials"); + } + Map userCredentials = serverController.getUserCredentials(userId, authProvider); + Object authToken = authProvider.getInstance().openSession( + Collections.emptyMap(), + userCredentials, + authParameters); + WebAuthInfo authInfo = new WebAuthInfo(); + authInfo.setUserId(userId); + authInfo.setLoginTime(OffsetDateTime.now()); + authInfo.setAuthProvider(authProvider.getId()); + authInfo.setAuthToken(authToken); + authInfo.setMessage("Logged using " + authProvider.getLabel() + " provider"); + + return authInfo; + } catch (DBException e) { + throw new DBWebException("User authentication failed", e); + } } @Override