mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
AWS auth provider. User auth + user registration
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
{
|
||||
// Server configuration
|
||||
"logView": {
|
||||
"logRefreshInterval": 3,
|
||||
"logBatchSize": 1000
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -50,10 +50,12 @@
|
||||
</service>
|
||||
|
||||
<authProvider id="local" label="Local" description="Local name/password based authentication" class="io.cloudbeaver.auth.provider.local.LocalAuthProvider">
|
||||
<propertyGroup label="General">
|
||||
<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" encryption="hash" admin="true" user="true"/>
|
||||
</propertyGroup>
|
||||
<credentials>
|
||||
<propertyGroup label="General">
|
||||
<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" encryption="hash" admin="true" user="true"/>
|
||||
</propertyGroup>
|
||||
</credentials>
|
||||
|
||||
</authProvider>
|
||||
</extension>
|
||||
|
||||
@@ -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<AUTH_SESSION> extends DBWAuthProvider<AUTH_SESSION> {
|
||||
|
||||
/**
|
||||
* Returns new identifying credentials which can be used to find/create user in database
|
||||
*/
|
||||
Map<String, Object> readExternalCredentials(
|
||||
Map<String, Object> providerConfig, // Auth provider configuration (e.g. 3rd party auth server address)
|
||||
Map<String, Object> authParameters // Passed auth parameters (e.g. user name or password)
|
||||
) throws DBException;
|
||||
|
||||
WebUser registerNewUser(DBWSecurityController securityController, Map<String, Object> providerConfig, Map<String, Object> credentials) throws DBException;
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String, Object> authParameters) throws DBCException;
|
||||
|
||||
/**
|
||||
|
||||
+28
-11
@@ -39,19 +39,32 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
|
||||
private ObjectType implType;
|
||||
private DBWAuthProvider instance;
|
||||
private final Map<String, WebAuthProviderPropertyDescriptor> properties = new LinkedHashMap<>();
|
||||
private final Map<String, PropertyDescriptor> configurationParameters = new LinkedHashMap<>();
|
||||
private final Map<String, WebAuthProviderPropertyDescriptor> 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<WebAuthProviderPropertyDescriptor> getProperties() {
|
||||
return new ArrayList<>(properties.values());
|
||||
public List<PropertyDescriptor> getConfigurationParameters() {
|
||||
return new ArrayList<>(configurationParameters.values());
|
||||
}
|
||||
|
||||
public WebAuthProviderPropertyDescriptor getProperty(String id) {
|
||||
return properties.get(id);
|
||||
public List<WebAuthProviderPropertyDescriptor> getCredentialParameters() {
|
||||
return new ArrayList<>(credentialParameters.values());
|
||||
}
|
||||
|
||||
public WebAuthProviderPropertyDescriptor getCredentialParameter(String id) {
|
||||
return credentialParameters.get(id);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,10 @@ public class CBApplication extends BaseApplicationImpl {
|
||||
return maxSessionIdleTime;
|
||||
}
|
||||
|
||||
public CBDatabaseConfig getDatabaseConfiguration() {
|
||||
return databaseConfiguration;
|
||||
}
|
||||
|
||||
public CBAppConfig getAppConfiguration() {
|
||||
return appConfiguration;
|
||||
}
|
||||
|
||||
+23
-11
@@ -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<String, Object> authParameters) throws DBCException {
|
||||
Map<String, Object> 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");
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ type AuthProviderInfo {
|
||||
|
||||
isDefault: Boolean
|
||||
|
||||
properties: [AuthCredentialInfo]!
|
||||
configurationParameters: [ObjectPropertyInfo]!
|
||||
credentialParameters: [AuthCredentialInfo]!
|
||||
}
|
||||
|
||||
type UserAuthInfo {
|
||||
|
||||
+21
-7
@@ -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<String, Object> providerConfig = Collections.emptyMap();
|
||||
|
||||
DBWAuthProvider<?> authProviderInstance = authProvider.getInstance();
|
||||
if (authProviderInstance instanceof DBWAuthProviderExternal<?>) {
|
||||
Map<String, Object> 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<String, Object> 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;
|
||||
|
||||
Reference in New Issue
Block a user