mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
#8 Authentication implementation
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
<authProvider id="local" label="Local" description="Local name/password based authentication" class="io.cloudbeaver.auth.provider.local.LocalAuthProvider">
|
||||
<propertyGroup label="General">
|
||||
<property id="name" label="User name" type="string" description="User name" admin="true" user="true"/>
|
||||
<property id="user" label="User name" type="string" description="User name" admin="true" user="true" identifying="true"/>
|
||||
<property id="password" label="User password" type="string" description="User password" admin="true" user="true"/>
|
||||
</propertyGroup>
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.util.Map;
|
||||
public interface DBWAuthProvider<AUTH_SESSION> {
|
||||
|
||||
AUTH_SESSION openSession(
|
||||
String userName,
|
||||
Map<String, Object> providerConfig, // Auth provider configuration (e.g. 3rd party auth server address)
|
||||
Map<String, Object> userCredentials, // Saved user credentials (e.g. associated 3rd party provider user name or realm)
|
||||
Map<String, Object> authParameters // Passed auth parameters (e.g. user name or password)
|
||||
|
||||
@@ -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<String, Object> credentials) throws DBCException;
|
||||
/**
|
||||
* Sets user redentials for specified provider
|
||||
*/
|
||||
void setUserCredentials(String userId, WebAuthProviderDescriptor authProvider, Map<String, Object> 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<String, Object> authParameters) throws DBCException;
|
||||
|
||||
/**
|
||||
* Get user credentials for specified provider
|
||||
*/
|
||||
Map<String, Object> getUserCredentials(String userId, WebAuthProviderDescriptor authProvider) throws DBCException;
|
||||
|
||||
WebRole[] readAllRoles() throws DBCException;
|
||||
|
||||
|
||||
@@ -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<String, Object> 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) {
|
||||
|
||||
+4
-2
@@ -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<LocalAuthToken> {
|
||||
|
||||
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<String, Object> providerConfig, Map<String, Object> userCredentials, Map<String, Object> authParameters) throws DBException {
|
||||
public LocalAuthToken openSession(Map<String, Object> providerConfig, Map<String, Object> userCredentials, Map<String, Object> 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)");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
+2
-3
@@ -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<DBPPropertyDescriptor> getProperties() {
|
||||
public List<WebAuthProviderPropertyDescriptor> getProperties() {
|
||||
return new ArrayList<>(properties.values());
|
||||
}
|
||||
|
||||
+7
-1
@@ -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;
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
+1
-1
@@ -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;
|
||||
@@ -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<String, Object> 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())) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+91
-11
@@ -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<String, Object> 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<String, Object> credentials) throws DBCException {
|
||||
List<String[]> 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<String, Object> authParameters) throws DBCException {
|
||||
Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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()) {
|
||||
|
||||
+2
-4
@@ -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;
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -26,9 +26,9 @@ type AuthProviderInfo {
|
||||
}
|
||||
|
||||
type UserAuthInfo {
|
||||
userName: String!
|
||||
|
||||
loginTime: DateTime
|
||||
userId: String!
|
||||
authProvider: String!
|
||||
loginTime: DateTime!
|
||||
|
||||
# Optional login message
|
||||
message: String
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ import java.util.Map;
|
||||
*/
|
||||
public interface DBWServiceAuth extends DBWService {
|
||||
|
||||
String authLogin(WebSession webSession, String providerId, Map<String, Object> credentials) throws DBWebException;
|
||||
WebAuthInfo authLogin(WebSession webSession, String providerId, Map<String, Object> credentials) throws DBWebException;
|
||||
|
||||
void authLogout(WebSession webSession) throws DBWebException;
|
||||
|
||||
|
||||
+28
-8
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+3
-10
@@ -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<DBWServiceAuth>
|
||||
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));
|
||||
|
||||
+40
-3
@@ -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<String, Object> credentials) throws DBWebException {
|
||||
return null;
|
||||
public WebAuthInfo authLogin(WebSession webSession, String providerId, Map<String, Object> 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<String, Object> 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
|
||||
|
||||
Reference in New Issue
Block a user