Merge remote-tracking branch 'origin/devel' into tech/CB-2186

This commit is contained in:
Ainur
2022-10-25 11:07:22 +02:00
43 changed files with 834 additions and 354 deletions
+6
View File
@@ -22,6 +22,12 @@ You can see live demo of CloudBeaver here: https://demo.cloudbeaver.io
## Changelog
### CloudBeaver 22.2.3 - 2022-10-14
- Users can specify the connection for the saved script in the Resource Manager. This connection will be selected by default when a user opens this script.
- The connection configuration through URL has been added for generic drivers.
- User access information can be stored in an SQL Server database.
- Different bugs have been fixed.
### CloudBeaver 22.2.2 - 2022-10-10
- Administrators now:
- have permission to create connections in both projects, Shared and Private, when it’s restricted for other users,
+2 -2
View File
@@ -1,13 +1,13 @@
{
teams: [
{
teamId: "admin",
subjectId: "admin",
name: "Admin",
description: "Administrative access. Has all permissions.",
permissions: [ "public", "admin" ]
},
{
teamId: "user",
subjectId: "user",
name: "User",
description: "Standard user",
permissions: [ "public" ]
@@ -18,7 +18,6 @@ Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport,
Export-Package: io.cloudbeaver,
io.cloudbeaver.auth,
io.cloudbeaver.auth.provider,
io.cloudbeaver.auth.provider.fa,
io.cloudbeaver.auth.provider.local,
io.cloudbeaver.events,
io.cloudbeaver.model,
@@ -3,5 +3,8 @@
<plugin>
<extension-point id="io.cloudbeaver.service" name="Web services" schema="schema/io.cloudbeaver.service.exsd"/>
<extension-point id="io.cloudbeaver.feature" name="Web features" schema="schema/io.cloudbeaver.feature.exsd"/>
<extension-point id="io.cloudbeaver.valueSerializer" name="Web value serializers" schema="schema/io.cloudbeaver.valueSerializer.exsd"/>
<extension-point id="org.jkiss.dbeaver.auth.provider" name="%extension-point.org.jkiss.dbeaver.authProvider.name" schema="schema/org.jkiss.dbeaver.authProvider.exsd"/>
<extension-point id="io.cloudbeaver.event.handler" name="Wev event handler" schema="schema/io.cloudbeaver.event.handler.exsd"/>
</plugin>
@@ -2,7 +2,7 @@
<schema targetNamespace="org.jkiss.dbeaver.core" xmlns="http://www.w3.org/2001/XMLSchema">
<annotation>
<appInfo>
<meta.schema plugin="io.cloudbeaver.server" id="io.cloudbeaver.feature" name="Web features"/>
<meta.schema plugin="io.cloudbeaver.model" id="io.cloudbeaver.feature" name="Web features"/>
</appInfo>
<documentation>
Web feature
@@ -0,0 +1,47 @@
<?xml version='1.0' encoding='UTF-8'?>
<schema targetNamespace="org.jkiss.dbeaver.core" xmlns="http://www.w3.org/2001/XMLSchema">
<annotation>
<appInfo>
<meta.schema plugin="io.cloudbeaver.model" id="org.jkiss.dbeaver.auth.provider" name="Authentication provider"/>
</appInfo>
<documentation>
DBeaver auth provider
</documentation>
</annotation>
<element name="extension">
<annotation>
<appInfo>
<meta.element />
</appInfo>
</annotation>
</element>
<element name="model">
<annotation>
<documentation>
Data source auth model description
</documentation>
</annotation>
</element>
<annotation>
<appInfo>
<meta.section type="appInfo"/>
</appInfo>
<documentation>
[Enter API information here.]
</documentation>
</annotation>
<annotation>
<appInfo>
<meta.section type="implementation"/>
</appInfo>
<documentation>
[Enter information about supplied implementation of this extension point.]
</documentation>
</annotation>
</schema>
@@ -17,8 +17,9 @@
package io.cloudbeaver;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
@@ -30,13 +31,21 @@ public class DBWUserIdentity {
@NotNull
private final String displayName;
private final Map<String, String> metaParameters = new HashMap<>();
private final Map<String, String> metaParameters = new LinkedHashMap<>();
@Nullable
private String providedUserRole;
@Nullable
private String[] providedTeamIds;
public DBWUserIdentity(@NotNull String id, @NotNull String displayName) {
this(id, displayName, Map.of());
}
public DBWUserIdentity(@NotNull String id, @NotNull String displayName, @NotNull Map<String, String> metaParameters) {
public DBWUserIdentity(
@NotNull String id,
@NotNull String displayName,
@NotNull Map<String, String> metaParameters
) {
this.id = id;
this.displayName = displayName;
this.metaParameters.putAll(metaParameters);
@@ -56,4 +65,30 @@ public class DBWUserIdentity {
public Map<String, String> getMetaParameters() {
return Map.copyOf(metaParameters);
}
public void setMetaParameter(@NotNull String name, @Nullable String value) {
if (value == null) {
metaParameters.remove(name);
} else {
metaParameters.put(name, value);
}
}
@Nullable
public String getProvidedUserRole() {
return providedUserRole;
}
public void setProvidedUserRole(@Nullable String providedUserRole) {
this.providedUserRole = providedUserRole;
}
@Nullable
public String[] getProvidedTeamIds() {
return providedTeamIds;
}
public void setProvidedTeamIds(@Nullable String[] providedTeamIds) {
this.providedTeamIds = providedTeamIds;
}
}
@@ -41,7 +41,7 @@ public class WebDataSourceRegistryProxy implements DBPDataSourceRegistry, DataSo
@Override
public DBPDataSourceContainer getDataSource(String id) {
DBPDataSourceContainer dataSource = dataSourceRegistry.getDataSource(id);
if (dataSourceFilter != null && !dataSourceFilter.filter(dataSource)) {
if (dataSource == null || dataSourceFilter != null && !dataSourceFilter.filter(dataSource)) {
return null;
}
return dataSource;
@@ -14,11 +14,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.user;
package io.cloudbeaver.auth;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import java.util.Map;
/**
* Web user.
* Auth
* Authenticates user using external user identity
*/
public class WebUserCredentials {
public interface SMAuthProviderAssigner {
@NotNull
SMAutoAssign detectAutoAssignments(
@NotNull DBRProgressMonitor monitor,
@NotNull Map<String, Object> providerConfig,
@NotNull Map<String, Object> authParameters
) throws DBException;
String getExternalTeamIdMetadataFieldName();
}
@@ -50,7 +50,7 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
DBWUserIdentity getUserIdentity(
@NotNull DBRProgressMonitor monitor,
@NotNull Map<String, Object> providerConfig,
@NotNull Map<String, Object> credentials) throws DBException;
@NotNull Map<String, Object> authParameters) throws DBException;
/**
* Get specified user details.
@@ -26,7 +26,7 @@ import java.util.Map;
* Federated auth provider.
* Provides links to external auth resource
*/
public interface SMWAuthProviderFederated {
public interface SMAuthProviderFederated {
/**
* Returns new identifying credentials which can be used to find/create user in database
@@ -0,0 +1,50 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.auth;
import java.util.ArrayList;
import java.util.List;
public class SMAutoAssign {
private String authRole;
private List<String> externalTeamIds = new ArrayList<>();
public SMAutoAssign() {
}
public SMAutoAssign(String authRole, List<String> externalRolesIds) {
this.authRole = authRole;
this.externalTeamIds = externalRolesIds;
}
public String getAuthRole() {
return authRole;
}
public void setAuthRole(String authRole) {
this.authRole = authRole;
}
public List<String> getExternalTeamIds() {
return externalTeamIds;
}
public void addExternalTeamId(String externalRoleId) {
this.externalTeamIds.add(externalRoleId);
}
}
@@ -1,69 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp
*
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of DBeaver Corp and its suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to DBeaver Corp and its suppliers
* and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from DBeaver Corp.
*/
package io.cloudbeaver.auth.provider.fa;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.auth.*;
import java.time.LocalDateTime;
import java.util.Map;
public abstract class AbstractSessionFederated implements SMSessionFederated {
@NotNull
protected final Map<String, Object> authParameters;
@NotNull
protected final SMSession parentSession;
@NotNull
protected final SMAuthSpace space;
protected AbstractSessionFederated(@NotNull SMSession parentSession, @NotNull SMAuthSpace space, @NotNull Map<String, Object> authParameters) {
this.parentSession = parentSession;
this.space = space;
this.authParameters = authParameters;
}
@NotNull
@Override
public SMAuthSpace getSessionSpace() {
return space;
}
@NotNull
@Override
public SMSessionContext getSessionContext() {
return this.parentSession.getSessionContext();
}
@Override
public SMSessionPrincipal getSessionPrincipal() {
return parentSession.getSessionPrincipal();
}
@NotNull
@Override
public LocalDateTime getSessionStart() {
return parentSession.getSessionStart();
}
@Override
public void close() {
// do nothing
}
}
@@ -1,47 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp
*
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of DBeaver Corp and its suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to DBeaver Corp and its suppliers
* and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from DBeaver Corp.
*/
package io.cloudbeaver.auth.provider.fa;
import io.cloudbeaver.model.user.WebUser;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.DBPObject;
import org.jkiss.dbeaver.model.meta.Property;
public class FederatedUserDetails implements DBPObject {
@NotNull
private AbstractSessionFederated session;
@NotNull
private WebUser user;
public FederatedUserDetails(@NotNull AbstractSessionFederated session, @NotNull WebUser user) {
this.session = session;
this.user = user;
}
@NotNull
public AbstractSessionFederated getSession() {
return session;
}
@Property
public String getEmail() {
return user.getUserId();
}
}
@@ -19,11 +19,11 @@ package io.cloudbeaver.model.app;
import com.google.gson.annotations.Expose;
import io.cloudbeaver.auth.provider.local.LocalAuthProviderConstants;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import org.jkiss.utils.ArrayUtils;
import java.util.*;
@@ -65,8 +65,8 @@ public abstract class BaseAuthWebAppConfiguration extends BaseWebAppConfiguratio
public String[] getEnabledAuthProviders() {
if (enabledAuthProviders == null) {
// No config - enable all providers (+backward compatibility)
return AuthProviderRegistry.getInstance().getAuthProviders()
.stream().map(AuthProviderDescriptor::getId).toArray(String[]::new);
return WebAuthProviderRegistry.getInstance().getAuthProviders()
.stream().map(WebAuthProviderDescriptor::getId).toArray(String[]::new);
}
return enabledAuthProviders;
}
@@ -78,7 +78,7 @@ public abstract class BaseAuthWebAppConfiguration extends BaseWebAppConfiguratio
@Override
public boolean isAuthProviderEnabled(String id) {
var authProviderDescriptor = AuthProviderRegistry.getInstance().getAuthProvider(id);
var authProviderDescriptor = WebAuthProviderRegistry.getInstance().getAuthProvider(id);
if (authProviderDescriptor == null) {
return false;
}
@@ -17,16 +17,16 @@
package io.cloudbeaver.model.session;
import io.cloudbeaver.DBWUserIdentity;
import io.cloudbeaver.model.user.WebAuthProviderConfiguration;
import io.cloudbeaver.model.user.WebUser;
import io.cloudbeaver.model.user.WebUserOriginInfo;
import io.cloudbeaver.registry.WebAuthProviderConfiguration;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
import org.jkiss.dbeaver.model.auth.SMSession;
import org.jkiss.dbeaver.model.auth.SMSessionPrincipal;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import java.time.OffsetDateTime;
import java.util.Map;
@@ -40,7 +40,7 @@ public class WebAuthInfo implements SMSessionPrincipal {
private final WebSession session;
private final WebUser user;
private final AuthProviderDescriptor authProvider;
private final WebAuthProviderDescriptor authProvider;
private WebAuthProviderConfiguration authProviderConfiguration;
private SMSession authSession;
private final OffsetDateTime loginTime;
@@ -52,7 +52,7 @@ public class WebAuthInfo implements SMSessionPrincipal {
public WebAuthInfo(
@NotNull WebSession session,
@NotNull WebUser user,
@NotNull AuthProviderDescriptor authProvider,
@NotNull WebAuthProviderDescriptor authProvider,
@NotNull DBWUserIdentity userIdentity,
@NotNull SMSession authSession,
@NotNull OffsetDateTime loginTime) {
@@ -112,7 +112,7 @@ public class WebAuthInfo implements SMSessionPrincipal {
return userIdentity;
}
public AuthProviderDescriptor getAuthProviderDescriptor() {
public WebAuthProviderDescriptor getAuthProviderDescriptor() {
return authProvider;
}
@@ -329,6 +329,10 @@ public class WebSession extends AbstractSessionPersistent
private void loadProjects() {
WebUser user = userContext.getUser();
if (user == null && DBWorkbench.isDistributed()) {
// No anonymous mode in distributed apps
return;
}
refreshAccessibleConnectionIds();
try {
RMController controller = application.getResourceController(this, getSecurityController());
@@ -23,6 +23,8 @@ import io.cloudbeaver.DBWebException;
import io.cloudbeaver.auth.SMAuthProviderExternal;
import io.cloudbeaver.model.app.WebAuthConfiguration;
import io.cloudbeaver.model.user.WebUser;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import io.cloudbeaver.utils.WebAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
@@ -31,8 +33,6 @@ import org.jkiss.dbeaver.model.auth.SMAuthInfo;
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
import org.jkiss.dbeaver.model.auth.SMSession;
import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import org.jkiss.utils.CommonUtils;
import java.time.OffsetDateTime;
@@ -192,8 +192,8 @@ public class WebSessionAuthProcessor {
}
}
private AuthProviderDescriptor getAuthProvider(String providerId) throws DBWebException {
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(providerId);
private WebAuthProviderDescriptor getAuthProvider(String providerId) throws DBWebException {
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(providerId);
if (authProvider == null) {
throw new DBWebException("Invalid auth provider '" + providerId + "'");
}
@@ -1,23 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.user;
public interface WebUserConstants {
String META_FIRST_NAME = "firstName";
String META_LAST_NAME = "lastName";
}
@@ -22,6 +22,7 @@ import io.cloudbeaver.model.WebObjectOrigin;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebAuthInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.utils.WebCommonUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -31,7 +32,6 @@ import org.jkiss.dbeaver.model.DBPObject;
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
import org.jkiss.dbeaver.model.auth.SMSession;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@@ -48,10 +48,10 @@ public class WebUserOriginInfo implements WebObjectOrigin {
private final WebSession session;
private final WebUser user;
@NotNull
private final AuthProviderDescriptor authProvider;
private final WebAuthProviderDescriptor authProvider;
private boolean selfIdentity;
public WebUserOriginInfo(WebSession session, WebUser user, @NotNull AuthProviderDescriptor authProvider, boolean selfIdentity) {
public WebUserOriginInfo(WebSession session, WebUser user, @NotNull WebAuthProviderDescriptor authProvider, boolean selfIdentity) {
this.session = session;
this.user = user;
this.authProvider = authProvider;
@@ -14,29 +14,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.user;
package io.cloudbeaver.registry;
import io.cloudbeaver.auth.SMWAuthProviderFederated;
import io.cloudbeaver.auth.SMAuthProviderFederated;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import java.util.Map;
/**
* WebAuthProviderInfo.
* WebAuthProviderConfiguration.
*/
public class WebAuthProviderConfiguration {
private static final Log log = Log.getLog(WebAuthProviderConfiguration.class);
private final AuthProviderDescriptor providerDescriptor;
private final WebAuthProviderDescriptor providerDescriptor;
private final SMAuthProviderCustomConfiguration config;
public WebAuthProviderConfiguration(AuthProviderDescriptor providerDescriptor, SMAuthProviderCustomConfiguration config) {
public WebAuthProviderConfiguration(WebAuthProviderDescriptor providerDescriptor, SMAuthProviderCustomConfiguration config) {
this.providerDescriptor = providerDescriptor;
this.config = config;
}
@@ -72,25 +71,25 @@ public class WebAuthProviderConfiguration {
@Property
public String getSignInLink() throws DBException {
SMAuthProvider<?> instance = providerDescriptor.getInstance();
return instance instanceof SMWAuthProviderFederated ? ((SMWAuthProviderFederated) instance).getSignInLink(getId(), config.getParameters()) : null;
return instance instanceof SMAuthProviderFederated ? ((SMAuthProviderFederated) instance).getSignInLink(getId(), config.getParameters()) : null;
}
@Property
public String getSignOutLink() throws DBException {
SMAuthProvider<?> instance = providerDescriptor.getInstance();
return instance instanceof SMWAuthProviderFederated ? ((SMWAuthProviderFederated) instance).getSignOutLink(getId(), config.getParameters()) : null;
return instance instanceof SMAuthProviderFederated ? ((SMAuthProviderFederated) instance).getSignOutLink(getId(), config.getParameters()) : null;
}
@Property
public String getRedirectLink() throws DBException {
SMAuthProvider<?> instance = providerDescriptor.getInstance();
return instance instanceof SMWAuthProviderFederated ? ((SMWAuthProviderFederated) instance).getRedirectLink(getId(), config.getParameters()) : null;
return instance instanceof SMAuthProviderFederated ? ((SMAuthProviderFederated) instance).getRedirectLink(getId(), config.getParameters()) : null;
}
@Property
public String getMetadataLink() throws DBException {
SMAuthProvider<?> instance = providerDescriptor.getInstance();
return instance instanceof SMWAuthProviderFederated ? ((SMWAuthProviderFederated) instance).getMetadataLink(getId(), config.getParameters()) : null;
return instance instanceof SMAuthProviderFederated ? ((SMAuthProviderFederated) instance).getMetadataLink(getId(), config.getParameters()) : null;
}
@Override
@@ -0,0 +1,189 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.registry;
import org.eclipse.core.runtime.IConfigurationElement;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPImage;
import org.jkiss.dbeaver.model.auth.AuthPropertyDescriptor;
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
import org.jkiss.dbeaver.model.impl.AbstractDescriptor;
import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.security.SMAuthCredentialsProfile;
import org.jkiss.dbeaver.model.security.SMAuthProviderDescriptor;
import org.jkiss.dbeaver.model.security.SMSubjectType;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
import java.util.*;
/**
* Auth provider descriptor
*/
public class WebAuthProviderDescriptor extends AbstractDescriptor {
public static final String EXTENSION_ID = "org.jkiss.dbeaver.auth.provider"; //$NON-NLS-1$
private static final Log log = Log.getLog(WebAuthProviderDescriptor.class);
private final IConfigurationElement cfg;
private final ObjectType implType;
private final Map<SMSubjectType, List<DBPPropertyDescriptor>> metaProperties = new HashMap<>();
private SMAuthProvider<?> instance;
private final DBPImage icon;
private final Map<String, PropertyDescriptor> configurationParameters = new LinkedHashMap<>();
private final List<SMAuthCredentialsProfile> credentialProfiles = new ArrayList<>();
private final boolean configurable;
private final boolean trusted;
private final String[] requiredFeatures;
public WebAuthProviderDescriptor(IConfigurationElement cfg) {
super(cfg);
this.cfg = cfg;
this.implType = new ObjectType(cfg, "class");
this.icon = iconToImage(cfg.getAttribute("icon"));
this.configurable = CommonUtils.toBoolean(cfg.getAttribute("configurable"));
this.trusted = CommonUtils.toBoolean(cfg.getAttribute("trusted"));
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")) {
credentialProfiles.add(new SMAuthCredentialsProfile(credElement));
}
for (IConfigurationElement mpElement : cfg.getChildren("metaProperties")) {
SMSubjectType subjectType = CommonUtils.valueOf(SMSubjectType.class, mpElement.getAttribute("type"), SMSubjectType.user);
List<DBPPropertyDescriptor> metaProps = new ArrayList<>();
for (IConfigurationElement propGroup : ArrayUtils.safeArray(mpElement.getChildren(PropertyDescriptor.TAG_PROPERTY_GROUP))) {
metaProps.addAll(PropertyDescriptor.extractProperties(propGroup));
}
metaProperties.put(subjectType, metaProps);
}
String rfList = cfg.getAttribute("requiredFeatures");
if (!CommonUtils.isEmpty(rfList)) {
requiredFeatures = rfList.split(",");
} else {
requiredFeatures = null;
}
}
@NotNull
public String getId() {
return cfg.getAttribute("id");
}
public String getLabel() {
return cfg.getAttribute("label");
}
public String getDescription() {
return cfg.getAttribute("description");
}
public DBPImage getIcon() {
return icon;
}
public boolean isConfigurable() {
return configurable;
}
public boolean isTrusted() {
return trusted;
}
public List<PropertyDescriptor> getConfigurationParameters() {
return new ArrayList<>(configurationParameters.values());
}
public List<SMAuthCredentialsProfile> getCredentialProfiles() {
return new ArrayList<>(credentialProfiles);
}
public List<AuthPropertyDescriptor> getCredentialParameters(String[] propNames) {
if (credentialProfiles.size() > 1) {
for (SMAuthCredentialsProfile profile : credentialProfiles) {
if (profile.getCredentialParameters().size() == propNames.length) {
boolean matches = true;
for (String paramName : propNames) {
if (profile.getCredentialParameter(paramName) == null) {
matches = false;
break;
}
}
if (matches) {
return profile.getCredentialParameters();
}
}
}
}
return credentialProfiles.get(0).getCredentialParameters();
}
@NotNull
public SMAuthProvider<?> getInstance() {
if (instance == null) {
try {
instance = implType.createInstance(SMAuthProvider.class);
} catch (DBException e) {
throw new IllegalStateException("Can not instantiate auth provider '" + implType.getImplName() + "'", e);
}
}
return instance;
}
public String[] getRequiredFeatures() {
return requiredFeatures;
}
@Override
public String toString() {
return getId();
}
public SMAuthProviderDescriptor createDescriptorBean() {
SMAuthProviderDescriptor smInfo = new SMAuthProviderDescriptor();
smInfo.setId(this.getId());
smInfo.setLabel(this.getLabel());
smInfo.setDescription(this.getDescription());
smInfo.setCredentialProfiles(this.getCredentialProfiles());
if (this.icon != null) {
smInfo.setIcon(icon.getLocation());
}
return smInfo;
}
@Nullable
public List<DBPPropertyDescriptor> getMetaProperties(SMSubjectType subjectType) {
return metaProperties.get(subjectType);
}
}
@@ -0,0 +1,85 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.registry;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.registry.RegistryConstants;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class WebAuthProviderRegistry {
private static final Log log = Log.getLog(WebAuthProviderRegistry.class);
private static final String TAG_AUTH_PROVIDER = "authProvider"; //$NON-NLS-1$
private static final String TAG_AUTH_PROVIDER_DISABLE = "authProviderDisable"; //$NON-NLS-1$
private static WebAuthProviderRegistry instance = null;
public synchronized static WebAuthProviderRegistry getInstance() {
if (instance == null) {
instance = new WebAuthProviderRegistry();
instance.loadExtensions(Platform.getExtensionRegistry());
}
return instance;
}
private final Map<String, WebAuthProviderDescriptor> authProviders = new LinkedHashMap<>();
private WebAuthProviderRegistry() {
}
private void loadExtensions(IExtensionRegistry registry) {
{
IConfigurationElement[] extConfigs = registry.getConfigurationElementsFor(WebAuthProviderDescriptor.EXTENSION_ID);
for (IConfigurationElement ext : extConfigs) {
// Load webServices
if (TAG_AUTH_PROVIDER.equals(ext.getName())) {
WebAuthProviderDescriptor providerDescriptor = new WebAuthProviderDescriptor(ext);
this.authProviders.put(providerDescriptor.getId(), providerDescriptor);
}
}
for (IConfigurationElement ext : extConfigs) {
// Disable auth providers
if (TAG_AUTH_PROVIDER_DISABLE.equals(ext.getName())) {
String providerId = ext.getAttribute(RegistryConstants.ATTR_ID);
if (!this.authProviders.containsKey(providerId)) {
log.warn("Can't disable auth provider '" + providerId + "' - no such provider found");
} else {
this.authProviders.remove(providerId);
}
}
}
}
}
public List<WebAuthProviderDescriptor> getAuthProviders() {
return new ArrayList<>(authProviders.values());
}
public WebAuthProviderDescriptor getAuthProvider(String id) {
return authProviders.get(id);
}
}
@@ -23,7 +23,6 @@ import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import java.util.*;
@@ -46,7 +45,7 @@ public class WebServiceRegistry {
private final List<WebServiceDescriptor> webServices = new ArrayList<>();
private DBWServiceBinding[] webServiceInstances;
private final Map<String, AuthProviderDescriptor> authProviders = new LinkedHashMap<>();
private final Map<String, WebAuthProviderDescriptor> authProviders = new LinkedHashMap<>();
private final Map<String, WebValueSerializerDescriptor> valueSerializers = new HashMap<>();
private WebServiceRegistry() {
@@ -1,4 +1,3 @@
Bundle-Vendor = DBeaver Corp
Bundle-Name = Cloudbeaver Web Server
meta.io.cloudbeaver.auth.provider.fa.FederatedUserDetails.email.name = E-Mail
@@ -3,9 +3,7 @@
<plugin>
<extension-point id="io.cloudbeaver.feature" name="Web features" schema="schema/io.cloudbeaver.feature.exsd"/>
<extension-point id="io.cloudbeaver.driver" name="Web drivers" schema="schema/io.cloudbeaver.driver.exsd"/>
<extension-point id="io.cloudbeaver.valueSerializer" name="Web value serializers" schema="schema/io.cloudbeaver.valueSerializer.exsd"/>
<extension-point id="io.cloudbeaver.handler" name="Web handlers" schema="schema/io.cloudbeaver.handler.exsd"/>
<extension point="org.eclipse.core.runtime.adapters">
@@ -24,6 +24,9 @@ import io.cloudbeaver.model.WebConnectionFolderInfo;
import io.cloudbeaver.model.WebNetworkHandlerConfigInput;
import io.cloudbeaver.model.session.WebActionParameters;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import io.cloudbeaver.server.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.utils.WebAppUtils;
import io.cloudbeaver.utils.WebCommonUtils;
@@ -55,9 +58,7 @@ import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
import org.jkiss.utils.CommonUtils;
import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.*;
/**
* Various constants
@@ -338,5 +339,17 @@ public class WebServiceUtils extends WebCommonUtils {
return project.getId().equals(RMProjectType.GLOBAL.getPrefix() + "_" + CBApplication.getInstance().getDefaultProjectName());
}
public static List<WebAuthProviderDescriptor> getEnabledAuthProviders() {
List<WebAuthProviderDescriptor> result = new ArrayList<>();
CBAppConfig appConfig = CBApplication.getInstance().getAppConfiguration();
String[] authProviders = appConfig.getEnabledAuthProviders();
for (String apId : authProviders) {
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(apId);
if (authProvider != null) {
result.add(authProvider);
}
}
return result;
}
}
@@ -17,12 +17,13 @@
package io.cloudbeaver.model.user;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.registry.WebAuthProviderConfiguration;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.security.SMAuthCredentialsProfile;
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import java.util.ArrayList;
import java.util.List;
@@ -34,13 +35,13 @@ public class WebAuthProviderInfo {
private static final Log log = Log.getLog(WebAuthProviderInfo.class);
private final AuthProviderDescriptor descriptor;
private final WebAuthProviderDescriptor descriptor;
public WebAuthProviderInfo(AuthProviderDescriptor descriptor) {
public WebAuthProviderInfo(WebAuthProviderDescriptor descriptor) {
this.descriptor = descriptor;
}
AuthProviderDescriptor getDescriptor() {
WebAuthProviderDescriptor getDescriptor() {
return descriptor;
}
@@ -20,12 +20,12 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.cloudbeaver.model.app.BaseAuthWebAppConfiguration;
import io.cloudbeaver.model.app.WebAuthConfiguration;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import org.jkiss.utils.CommonUtils;
import java.util.LinkedHashMap;
@@ -171,8 +171,8 @@ public class CBAppConfig extends BaseAuthWebAppConfiguration implements WebAuthC
}
public String[] getAllAuthProviders() {
return AuthProviderRegistry.getInstance().getAuthProviders()
.stream().map(AuthProviderDescriptor::getId).toArray(String[]::new);
return WebAuthProviderRegistry.getInstance().getAuthProviders()
.stream().map(WebAuthProviderDescriptor::getId).toArray(String[]::new);
}
public DBNBrowseSettings getDefaultNavigatorSettings() {
@@ -2,9 +2,11 @@ package io.cloudbeaver.server.servlets;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.auth.SMWAuthProviderFederated;
import io.cloudbeaver.auth.SMAuthProviderFederated;
import io.cloudbeaver.model.session.WebActionParameters;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import io.cloudbeaver.registry.WebHandlerRegistry;
import io.cloudbeaver.registry.WebServletHandlerDescriptor;
import io.cloudbeaver.server.CBAppConfig;
@@ -21,8 +23,6 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMAuthInfo;
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
@@ -86,7 +86,7 @@ public class CBStaticServlet extends DefaultServlet {
String[] authProviders = appConfig.getEnabledAuthProviders();
if (authProviders.length == 1) {
String authProviderId = authProviders[0];
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(authProviderId);
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(authProviderId);
if (authProvider != null && authProvider.isConfigurable()) {
SMAuthProviderCustomConfiguration activeAuthConfig = null;
for (SMAuthProviderCustomConfiguration cfg : appConfig.getAuthCustomConfigurations()) {
@@ -105,7 +105,7 @@ public class CBStaticServlet extends DefaultServlet {
// We have the only provider
// Forward to signon URL
SMAuthProvider<?> authProviderInstance = authProvider.getInstance();
if (authProviderInstance instanceof SMWAuthProviderFederated) {
if (authProviderInstance instanceof SMAuthProviderFederated) {
if (webSession.getUser() == null) {
var securityController = webSession.getSecurityController();
SMAuthInfo authInfo = securityController.authenticate(
@@ -20,13 +20,13 @@ import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.user.WebUser;
import io.cloudbeaver.model.user.WebUserOriginInfo;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.security.SMDataSourceGrant;
import org.jkiss.dbeaver.model.security.SMObjects;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import java.util.ArrayList;
import java.util.List;
@@ -96,7 +96,7 @@ public class AdminUserInfo {
public WebUserOriginInfo[] getOrigins() throws DBWebException {
List<WebUserOriginInfo> result = new ArrayList<>();
for (String provider : getUserLinkedProviders()) {
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(provider);
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(provider);
if (authProvider == null) {
log.error("Auth provider '" + provider + "' not found");
} else {
@@ -24,7 +24,7 @@ import io.cloudbeaver.model.WebConnectionConfig;
import io.cloudbeaver.model.WebConnectionInfo;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.user.WebAuthProviderConfiguration;
import io.cloudbeaver.registry.WebAuthProviderConfiguration;
import io.cloudbeaver.service.DBWService;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -189,8 +189,7 @@ public interface DBWServiceAdmin extends DBWService {
Boolean deleteUserMetaParameter(WebSession webSession, String id) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
Boolean setUserMetaParameterValues(WebSession webSession, String userId, Map<String, Object> parameters) throws DBWebException;
Boolean setUserMetaParameterValues(WebSession webSession, String userId, Map<String, String> parameters) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
Boolean enableUser(WebSession webSession, String userId, Boolean enabled) throws DBWebException;
@@ -25,11 +25,8 @@ import io.cloudbeaver.model.WebConnectionInfo;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebAuthInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.user.WebAuthProviderConfiguration;
import io.cloudbeaver.model.user.WebUser;
import io.cloudbeaver.registry.WebFeatureRegistry;
import io.cloudbeaver.registry.WebPermissionDescriptor;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.registry.*;
import io.cloudbeaver.server.CBAppConfig;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
@@ -56,8 +53,6 @@ import org.jkiss.dbeaver.model.security.SMObjects;
import org.jkiss.dbeaver.model.security.user.SMTeam;
import org.jkiss.dbeaver.model.security.user.SMUser;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import org.jkiss.utils.CommonUtils;
import java.text.MessageFormat;
@@ -293,7 +288,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
@Override
public boolean setUserCredentials(@NotNull WebSession webSession, @NotNull String userID, @NotNull String providerId, @NotNull Map<String, Object> credentials) throws DBWebException {
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(providerId);
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(providerId);
if (authProvider == null) {
throw new DBWebException("Invalid auth provider '" + providerId + "'");
}
@@ -492,11 +487,16 @@ public class WebServiceAdmin implements DBWServiceAdmin {
@Override
public List<WebPropertyInfo> listAuthProviderConfigurationParameters(@NotNull WebSession webSession, @NotNull String providerId) throws DBWebException {
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(providerId);
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(providerId);
if (authProvider == null) {
throw new DBWebException("Invalid provider ID " + providerId);
}
return authProvider.getConfigurationParameters().stream().map(p -> new WebPropertyInfo(webSession, p)).collect(Collectors.toList());
return authProvider.getConfigurationParameters().stream().filter(p -> {
if (p.hasFeature("distributed")) {
return CBApplication.getInstance().isDistributed();
}
return true;
}).map(p -> new WebPropertyInfo(webSession, p)).collect(Collectors.toList());
}
@Override
@@ -506,7 +506,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
if (providerId != null && !providerId.equals(cfg.getProvider())) {
continue;
}
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(cfg.getProvider());
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(cfg.getProvider());
if (authProvider != null) {
result.add(new WebAuthProviderConfiguration(authProvider, cfg));
}
@@ -524,7 +524,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
@Nullable String iconURL,
@Nullable String description,
@Nullable Map<String, Object> parameters) throws DBWebException {
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(providerId);
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(providerId);
if (authProvider == null) {
throw new DBWebException("Auth provider '" + providerId + "' not found");
}
@@ -779,9 +779,9 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
@Override
public Boolean setUserMetaParameterValues(WebSession webSession, String userId, Map<String, Object> parameters) throws DBWebException {
public Boolean setUserMetaParameterValues(WebSession webSession, String userId, Map<String, String> parameters) throws DBWebException {
try {
webSession.getAdminSecurityController().setUserMeta(userId, parameters);
webSession.getAdminSecurityController().setSubjectMetas(userId, parameters);
return true;
} catch (DBException e) {
throw new DBWebException("Error changing user '" + userId + "' meta parameters", e);
@@ -22,6 +22,8 @@ import io.cloudbeaver.auth.provider.rp.RPAuthProvider;
import io.cloudbeaver.model.app.WebAuthConfiguration;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.session.WebSessionAuthProcessor;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.service.DBWSessionHandler;
import io.cloudbeaver.utils.WebAppUtils;
@@ -32,8 +34,6 @@ import org.jkiss.dbeaver.model.auth.SMAuthInfo;
import org.jkiss.dbeaver.model.security.SMConstants;
import org.jkiss.dbeaver.model.security.SMController;
import org.jkiss.dbeaver.model.security.exception.SMException;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -61,7 +61,7 @@ public class RPSessionHandler implements DBWSessionHandler {
public void reverseProxyAuthentication(@NotNull HttpServletRequest request, @NotNull WebSession webSession) throws DBWebException {
SMController securityController = webSession.getSecurityController();
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(RPAuthProvider.AUTH_PROVIDER);
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(RPAuthProvider.AUTH_PROVIDER);
if (authProvider == null) {
throw new DBWebException("Auth provider " + RPAuthProvider.AUTH_PROVIDER + " not found");
}
@@ -17,6 +17,7 @@
package io.cloudbeaver.service.auth.impl;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebAuthInfo;
@@ -24,6 +25,8 @@ import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.session.WebSessionAuthProcessor;
import io.cloudbeaver.model.user.WebAuthProviderInfo;
import io.cloudbeaver.model.user.WebUser;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import io.cloudbeaver.registry.WebUserProfileRegistry;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.service.auth.DBWServiceAuth;
@@ -35,11 +38,13 @@ import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMAuthInfo;
import org.jkiss.dbeaver.model.auth.SMAuthStatus;
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.security.SMController;
import org.jkiss.dbeaver.model.security.SMSubjectType;
import org.jkiss.dbeaver.model.security.user.SMUser;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import org.jkiss.utils.CommonUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -158,7 +163,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
@Override
public WebAuthProviderInfo[] getAuthProviders() {
return AuthProviderRegistry.getInstance().getAuthProviders()
return WebAuthProviderRegistry.getInstance().getAuthProviders()
.stream().map(WebAuthProviderInfo::new)
.toArray(WebAuthProviderInfo[]::new);
}
@@ -177,7 +182,20 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
@Override
public WebPropertyInfo[] listUserProfileProperties(@NotNull WebSession webSession) {
return WebUserProfileRegistry.getInstance().getProperties().stream()
List<DBPPropertyDescriptor> props = new ArrayList<>();
// First add user profile properties
props.addAll(WebUserProfileRegistry.getInstance().getProperties());
// Add metas from enabled auth providers
for (WebAuthProviderDescriptor ap : WebServiceUtils.getEnabledAuthProviders()) {
List<DBPPropertyDescriptor> metaProps = ap.getMetaProperties(SMSubjectType.user);
if (!CommonUtils.isEmpty(metaProps)) {
props.addAll(metaProps);
}
}
return props.stream()
.map(p -> new WebPropertyInfo(webSession, p, null))
.toArray(WebPropertyInfo[]::new);
}
@@ -66,6 +66,18 @@ CREATE TABLE CB_AUTH_SUBJECT
PRIMARY KEY (SUBJECT_ID)
);
CREATE TABLE CB_SUBJECT_META
(
SUBJECT_ID VARCHAR(128) NOT NULL,
META_ID VARCHAR(32) NOT NULL,
META_VALUE VARCHAR(1024),
UPDATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (SUBJECT_ID, META_ID),
FOREIGN KEY (SUBJECT_ID) REFERENCES CB_AUTH_SUBJECT(SUBJECT_ID) ON DELETE CASCADE
);
CREATE TABLE CB_AUTH_PERMISSIONS
(
SUBJECT_ID VARCHAR(128) NOT NULL,
@@ -106,15 +118,6 @@ CREATE TABLE CB_USER
);
-- Additional user properties (profile)
CREATE TABLE CB_USER_META
(
USER_ID VARCHAR(128) 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_USER_PARAMETERS
(
@@ -299,9 +302,9 @@ CREATE INDEX CB_SESSION_LOG_INDEX ON CB_SESSION_LOG (SESSION_ID, LOG_TIME);
CREATE TABLE CB_USER_SECRETS
(
USER_ID VARCHAR(128) NOT NULL,
SECRET_ID VARCHAR(512) NOT NULL,
SECRET_VALUE TEXT NOT NULL,
USER_ID VARCHAR(128) NOT NULL,
SECRET_ID VARCHAR(512) NOT NULL,
SECRET_VALUE TEXT NOT NULL,
SECRET_LABEL VARCHAR(128),
SECRET_DESCRIPTION VARCHAR(1024),
@@ -0,0 +1,16 @@
CREATE TABLE CB_SUBJECT_META
(
SUBJECT_ID VARCHAR(128) NOT NULL,
META_ID VARCHAR(32) NOT NULL,
META_VALUE VARCHAR(1024),
UPDATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (SUBJECT_ID, META_ID),
FOREIGN KEY (SUBJECT_ID) REFERENCES CB_AUTH_SUBJECT(SUBJECT_ID) ON DELETE CASCADE
);
INSERT INTO CB_SUBJECT_META (SUBJECT_ID, META_ID, META_VALUE)
SELECT USER_ID, META_ID, META_VALUE FROM CB_USER_META;
DROP TABLE CB_USER_META;
@@ -17,6 +17,8 @@
package io.cloudbeaver.auth.provider.local;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
@@ -25,8 +27,6 @@ import org.jkiss.dbeaver.model.auth.SMAuthProvider;
import org.jkiss.dbeaver.model.auth.SMSession;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.security.SMController;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import org.jkiss.utils.CommonUtils;
import java.util.Map;
@@ -51,7 +51,7 @@ public class LocalAuthProvider implements SMAuthProvider<LocalAuthSession> {
String userName = CommonUtils.toString(userCredentials.get(CRED_USER), null);
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(PROVIDER_ID);
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(PROVIDER_ID);
Map<String, Object> storedCredentials = securityController.getUserCredentials(userName, authProvider.getId());
if (storedCredentials == null) {
throw new DBException("Invalid user name or password");
@@ -95,7 +95,7 @@ public class LocalAuthProvider implements SMAuthProvider<LocalAuthSession> {
public static boolean changeUserPassword(@NotNull WebSession webSession, @NotNull String oldPassword, @NotNull String newPassword) throws DBException {
String userName = webSession.getUser().getUserId();
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(PROVIDER_ID);
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(PROVIDER_ID);
Map<String, Object> storedCredentials = webSession.getSecurityController().getUserCredentials(userName, authProvider.getId());
if (storedCredentials == null) {
throw new DBException("Invalid user name or password");
@@ -65,8 +65,8 @@ public class RPAuthProvider implements SMAuthProviderExternal<SMSession> {
@NotNull
@Override
public DBWUserIdentity getUserIdentity(@NotNull DBRProgressMonitor monitor, @NotNull Map<String, Object> providerConfig, @NotNull Map<String, Object> credentials) throws DBException {
String userName = String.valueOf(credentials.get("user"));
public DBWUserIdentity getUserIdentity(@NotNull DBRProgressMonitor monitor, @NotNull Map<String, Object> providerConfig, @NotNull Map<String, Object> authParameters) throws DBException {
String userName = String.valueOf(authParameters.get("user"));
return new DBWUserIdentity(userName, userName);
}
@@ -20,12 +20,16 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.auth.SMAuthProviderAssigner;
import io.cloudbeaver.auth.SMAuthProviderExternal;
import io.cloudbeaver.auth.SMWAuthProviderFederated;
import io.cloudbeaver.auth.SMAuthProviderFederated;
import io.cloudbeaver.auth.SMAutoAssign;
import io.cloudbeaver.model.app.WebApplication;
import io.cloudbeaver.model.app.WebAuthApplication;
import io.cloudbeaver.model.app.WebAuthConfiguration;
import io.cloudbeaver.model.session.WebAuthInfo;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import io.cloudbeaver.service.security.db.CBDatabase;
import io.cloudbeaver.service.security.internal.AuthAttemptSessionInfo;
import io.cloudbeaver.service.security.internal.SMTokenInfo;
@@ -40,17 +44,11 @@ import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils;
import org.jkiss.dbeaver.model.impl.jdbc.exec.JDBCTransaction;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.runtime.LoggingProgressMonitor;
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
import org.jkiss.dbeaver.model.security.*;
import org.jkiss.dbeaver.model.security.exception.SMAccessTokenExpiredException;
import org.jkiss.dbeaver.model.security.exception.SMException;
import org.jkiss.dbeaver.model.security.exception.SMRefreshTokenExpiredException;
import org.jkiss.dbeaver.model.security.user.SMAuthPermissions;
import org.jkiss.dbeaver.model.security.user.SMObjectPermissions;
import org.jkiss.dbeaver.model.security.user.SMTeam;
import org.jkiss.dbeaver.model.security.user.SMUser;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import org.jkiss.dbeaver.model.security.user.*;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
@@ -139,16 +137,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
dbStat.execute();
}
if (!CommonUtils.isEmpty(metaParameters)) {
try (PreparedStatement dbStat = dbCon.prepareStatement("INSERT INTO CB_USER_META(USER_ID,META_ID,META_VALUE) VALUES(?,?,?)")) {
dbStat.setString(1, userId);
for (Map.Entry<String, String> mp : metaParameters.entrySet()) {
dbStat.setString(2, mp.getKey());
dbStat.setString(3, mp.getValue());
dbStat.execute();
}
}
}
saveSubjectMetas(dbCon, userId, metaParameters);
txn.commit();
}
} catch (SQLException e) {
@@ -158,6 +147,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
@Override
public void deleteUser(String userId) throws DBCException {
invalidateAllUserTokens(userId);
try (Connection dbCon = database.openConnection()) {
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
deleteAuthSubject(dbCon, userId);
@@ -232,18 +222,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
}
}
// Metas
try (PreparedStatement dbStat = dbCon.prepareStatement("SELECT META_ID,META_VALUE FROM CB_USER_META WHERE USER_ID=?")) {
dbStat.setString(1, userId);
try (ResultSet dbResult = dbStat.executeQuery()) {
while (dbResult.next()) {
user.setMetaParameter(
dbResult.getString(1),
dbResult.getString(2)
);
}
}
}
readSubjectMetas(dbCon, user);
// Teams
try (PreparedStatement dbStat = dbCon.prepareStatement("SELECT TEAM_ID FROM CB_USER_TEAM WHERE USER_ID=?")) {
dbStat.setString(1, userId);
@@ -282,25 +261,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
}
}
// Read metas
try (PreparedStatement dbStat = dbCon.prepareStatement("SELECT USER_ID,META_ID,META_VALUE FROM CB_USER_META" +
(CommonUtils.isEmpty(userNameMask) ? "" : " WHERE USER_ID=?"))) {
if (!CommonUtils.isEmpty(userNameMask)) {
dbStat.setString(1, userNameMask);
}
try (ResultSet dbResult = dbStat.executeQuery()) {
while (dbResult.next()) {
String userId = dbResult.getString(1);
SMUser user = result.get(userId);
if (user != null) {
user.setMetaParameter(
dbResult.getString(2),
dbResult.getString(3)
);
}
}
}
}
readSubjectsMetas(dbCon, SMSubjectType.user, userNameMask, result);
// Read teams
try (PreparedStatement dbStat = dbCon.prepareStatement("SELECT USER_ID,TEAM_ID FROM CB_USER_TEAM" +
(CommonUtils.isEmpty(userNameMask) ? "" : " WHERE USER_ID=?"))) {
@@ -325,30 +286,67 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
}
@Override
public void setUserMeta(String userId, Map<String, Object> metaParameters) throws DBCException {
try (Connection dbCon = database.openConnection()) {
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
// Delete old metas
try (PreparedStatement dbStat = dbCon.prepareStatement("DELETE FROM CB_USER_META WHERE USER_ID=?")) {
dbStat.setString(1, userId);
dbStat.execute();
private void cleanupSubjectMeta(Connection dbCon, String subjectId) throws SQLException {
// Delete old metas
try (PreparedStatement dbStat = dbCon.prepareStatement(
"DELETE FROM CB_SUBJECT_META WHERE SUBJECT_ID=?")) {
dbStat.setString(1, subjectId);
dbStat.execute();
}
}
private void readSubjectMetas(Connection dbCon, SMSubject subject) throws SQLException {
// Metas
try (PreparedStatement dbStat = dbCon.prepareStatement(
"SELECT META_ID,META_VALUE FROM CB_SUBJECT_META WHERE SUBJECT_ID=?")) {
dbStat.setString(1, subject.getSubjectId());
try (ResultSet dbResult = dbStat.executeQuery()) {
while (dbResult.next()) {
subject.setMetaParameter(
dbResult.getString(1),
dbResult.getString(2)
);
}
if (!metaParameters.isEmpty()) {
// Insert new metas
try (PreparedStatement dbStat = dbCon.prepareStatement("INSERT INTO CB_USER_META(USER_ID,META_ID,META_VALUE) VALUES(?,?,?)")) {
dbStat.setString(1, userId);
for (Map.Entry<String, Object> mpe : metaParameters.entrySet()) {
dbStat.setString(2, mpe.getKey());
dbStat.setString(3, CommonUtils.toString(mpe.getValue()));
dbStat.execute();
}
}
}
}
private void readSubjectsMetas(Connection dbCon, SMSubjectType subjectType, String nameMask, Map<String, ? extends SMSubject> result) throws SQLException {
// Read metas
try (PreparedStatement dbStat = dbCon.prepareStatement(
"SELECT m.SUBJECT_ID,m.META_ID,m.META_VALUE FROM CB_AUTH_SUBJECT s, CB_SUBJECT_META m\n" +
"WHERE s.SUBJECT_TYPE=? AND s.SUBJECT_ID=m.SUBJECT_ID" +
(CommonUtils.isEmpty(nameMask) ? "" : " AND s.SUBJECT_ID=?"))) {
dbStat.setString(1, subjectType.getCode());
if (!CommonUtils.isEmpty(nameMask)) {
dbStat.setString(2, nameMask);
}
try (ResultSet dbResult = dbStat.executeQuery()) {
while (dbResult.next()) {
String subjectId = dbResult.getString(1);
SMSubject subject = result.get(subjectId);
if (subject != null) {
subject.setMetaParameter(
dbResult.getString(2),
dbResult.getString(3)
);
}
}
txn.commit();
}
} catch (SQLException e) {
throw new DBCException("Error while loading users", e);
}
}
private void saveSubjectMetas(Connection dbCon, String subjectId, Map<String, String> metaParameters) throws SQLException {
if (!CommonUtils.isEmpty(metaParameters)) {
try (PreparedStatement dbStat = dbCon.prepareStatement(
"INSERT INTO CB_SUBJECT_META(SUBJECT_ID,META_ID,META_VALUE) VALUES(?,?,?)")) {
dbStat.setString(1, subjectId);
for (Map.Entry<String, String> mp : metaParameters.entrySet()) {
dbStat.setString(2, mp.getKey());
dbStat.setString(3, mp.getValue());
dbStat.execute();
}
}
}
}
@@ -439,7 +437,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
///////////////////////////////////////////
// Credentials
private static SMAuthCredentialsProfile getCredentialProfileByParameters(AuthProviderDescriptor authProvider, Set<String> keySet) {
private static SMAuthCredentialsProfile getCredentialProfileByParameters(WebAuthProviderDescriptor authProvider, Set<String> keySet) {
List<SMAuthCredentialsProfile> credentialProfiles = authProvider.getCredentialProfiles();
if (credentialProfiles.size() > 1) {
for (SMAuthCredentialsProfile profile : credentialProfiles) {
@@ -462,12 +460,12 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
@Override
public void setUserCredentials(@NotNull String userId, @NotNull String authProviderId, @NotNull Map<String, Object> credentials) throws DBException {
var existUserByCredentials = findUserByCredentials(authProviderId, credentials);
var existUserByCredentials = findUserByCredentials(getAuthProvider(authProviderId), credentials);
if (existUserByCredentials != null && !existUserByCredentials.equals(userId)) {
throw new DBException("Another user is already linked to the specified credentials");
}
List<String[]> transformedCredentials;
AuthProviderDescriptor authProvider = getAuthProvider(authProviderId);
WebAuthProviderDescriptor authProvider = getAuthProvider(authProviderId);
try {
SMAuthCredentialsProfile credProfile = getCredentialProfileByParameters(authProvider, credentials.keySet());
transformedCredentials = credentials.entrySet().stream().map(cred -> {
@@ -508,10 +506,10 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
@Nullable
private String findUserByCredentials(String authProviderId, Map<String, Object> authParameters) throws DBCException {
AuthProviderDescriptor authProvider = getAuthProvider(authProviderId);
private String findUserByCredentials(WebAuthProviderDescriptor authProvider, Map<String, Object> authParameters) throws DBCException {
Map<String, Object> identCredentials = new LinkedHashMap<>();
for (AuthPropertyDescriptor prop : authProvider.getCredentialParameters(authParameters.keySet())) {
String[] propNames = authParameters.keySet().toArray(new String[0]);
for (AuthPropertyDescriptor prop : authProvider.getCredentialParameters(propNames)) {
if (prop.isIdentifying()) {
String propId = CommonUtils.toString(prop.getId());
Object paramValue = authParameters.get(propId);
@@ -577,7 +575,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
@Override
public Map<String, Object> getUserCredentials(String userId, String authProviderId) throws DBCException {
AuthProviderDescriptor authProvider = getAuthProvider(authProviderId);
WebAuthProviderDescriptor authProvider = getAuthProvider(authProviderId);
try (Connection dbCon = database.openConnection()) {
try (PreparedStatement dbStat = dbCon.prepareStatement(
"SELECT CRED_ID,CRED_VALUE FROM CB_USER_CREDENTIALS\n" +
@@ -650,6 +648,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
}
}
readSubjectsMetas(dbCon, SMSubjectType.team,null, teams);
return teams.values().toArray(new SMTeam[0]);
} catch (SQLException e) {
throw new DBCException("Error reading teams from database", e);
@@ -773,7 +772,22 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
///////////////////////////////////////////
// Permissions
// Subject functions
@Override
public void setSubjectMetas(String userId, Map<String, String> metaParameters) throws DBCException {
try (Connection dbCon = database.openConnection()) {
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
cleanupSubjectMeta(dbCon, userId);
if (!metaParameters.isEmpty()) {
saveSubjectMetas(dbCon, userId, metaParameters);
}
txn.commit();
}
} catch (SQLException e) {
throw new DBCException("Error while loading users", e);
}
}
@Override
public void setSubjectPermissions(String subjectId, List<String> permissionIds, String grantorId) throws DBException {
@@ -920,7 +934,8 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
smTokens.getSmAccessToken(),
smTokens.getSmRefreshToken(),
new SMAuthPermissions(null, smSessionId, permissions),
Map.of()
Map.of(),
smConfig.getDefaultAuthRole()
);
}
} catch (SQLException e) {
@@ -943,11 +958,11 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
@Nullable String authProviderConfigurationId,
@NotNull Map<String, Object> userCredentials
) throws DBException {
var authProgressMonitor = new VoidProgressMonitor();
var authProgressMonitor = new LoggingProgressMonitor(log);
try (Connection dbCon = database.openConnection()) {
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
Map<String, Object> securedUserIdentifyingCredentials = userCredentials;
AuthProviderDescriptor authProviderDescriptor = getAuthProvider(authProviderId);
WebAuthProviderDescriptor authProviderDescriptor = getAuthProvider(authProviderId);
var authProviderInstance = authProviderDescriptor.getInstance();
if (SMAuthProviderExternal.class.isAssignableFrom(authProviderInstance.getClass())) {
var authProviderExternal = (SMAuthProviderExternal<?>) authProviderInstance;
@@ -970,9 +985,9 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
sessionParameters
);
if (SMWAuthProviderFederated.class.isAssignableFrom(authProviderInstance.getClass())) {
if (SMAuthProviderFederated.class.isAssignableFrom(authProviderInstance.getClass())) {
//async auth
var authProviderFederated = (SMWAuthProviderFederated) authProviderInstance;
var authProviderFederated = (SMAuthProviderFederated) authProviderInstance;
var redirectUrl = buildRedirectLink(
authProviderFederated.getSignInLink(authProviderConfigurationId, Map.of()),
authAttemptId);
@@ -980,7 +995,11 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
txn.commit();
return finishAuthentication(
SMAuthInfo.inProgress(authAttemptId, null, Map.of(authProviderId, securedUserIdentifyingCredentials)),
SMAuthInfo.inProgress(
authAttemptId,
null,
Map.of(authProviderId, securedUserIdentifyingCredentials)
),
true,
false
);
@@ -992,7 +1011,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
private Map<String, Object> filterSecuredUserData(
Map<String, Object> userIdentifyingCredentials,
AuthProviderDescriptor authProviderDescriptor
WebAuthProviderDescriptor authProviderDescriptor
) {
SMAuthCredentialsProfile credProfile = getCredentialProfileByParameters(authProviderDescriptor, userIdentifyingCredentials.keySet());
return userIdentifyingCredentials.entrySet()
@@ -1133,8 +1152,10 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
Map<String, Object> authData = new LinkedHashMap<>();
String redirectUrl = null;
try (PreparedStatement dbStat = dbCon.prepareStatement(
"SELECT AUTH_PROVIDER_ID,AUTH_PROVIDER_CONFIGURATION_ID,AUTH_STATE FROM CB_AUTH_ATTEMPT_INFO "
"SELECT AUTH_PROVIDER_ID,AUTH_PROVIDER_CONFIGURATION_ID,AUTH_STATE " +
"FROM CB_AUTH_ATTEMPT_INFO "
+ "WHERE AUTH_ID=? ORDER BY CREATE_TIME"
)) {
dbStat.setString(1, authId);
@@ -1144,13 +1165,15 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
String authProviderConfiguration = dbResult.getString(2);
Map<String, Object> authProviderData = gson.fromJson(dbResult.getString(3), MAP_STRING_OBJECT_TYPE);
if (authProviderConfiguration != null) {
var authProviderInstance = getAuthProvider(authProviderId).getInstance();
if (SMWAuthProviderFederated.class.isAssignableFrom(authProviderInstance.getClass())) {
WebAuthProviderDescriptor authProviderDescriptor = getAuthProvider(authProviderId);
var authProviderInstance = authProviderDescriptor.getInstance();
if (SMAuthProviderFederated.class.isAssignableFrom(authProviderInstance.getClass())) {
redirectUrl = buildRedirectLink(
((SMWAuthProviderFederated) authProviderInstance).getRedirectLink(authProviderConfiguration, Map.of()),
((SMAuthProviderFederated) authProviderInstance).getRedirectLink(authProviderConfiguration, Map.of()),
authId
);
}
}
authData.put(authProviderId, authProviderData);
}
@@ -1172,12 +1195,15 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
SMTokens smTokens = findTokenBySmSession(smSessionId);
SMAuthPermissions authPermissions = getTokenPermissions(smTokens.getSmAccessToken());
String userId = authPermissions.getUserId();
String authRole = readTokenAuthRole(smTokens.getSmAccessToken());
var successAuthStatus = SMAuthInfo.success(
authId,
smTokens.getSmAccessToken(),
smTokens.getSmRefreshToken(),
authPermissions,
authData
authData,
authRole
);
updateAuthStatus(authId, SMAuthStatus.EXPIRED, authData, null, authPermissions.getSessionId());
return successAuthStatus;
@@ -1221,6 +1247,14 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
}
private void invalidateAllUserTokens(String userId) throws DBCException {
try (Connection dbCon = database.openConnection()) {
JDBCUtils.executeStatement(dbCon, "DELETE FROM CB_AUTH_TOKEN WHERE USER_ID=?", userId);
} catch (SQLException e) {
throw new DBCException("Session invalidation failed", e);
}
}
private SMCredentials getCurrentUserCreds() throws SMException {
var currentUserCreds = credentialsProvider.getActiveUserCredentials();
if (currentUserCreds == null) {
@@ -1305,16 +1339,24 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
String activeUserId = permissions == null ? null : permissions.getUserId();
Map<String, Object> storedUserData = new LinkedHashMap<>();
SMTeam[] allTeams = null;
String detectedAuthRole = null;
for (String authProviderId : authProviderIds) {
WebAuthProviderDescriptor authProvider = getAuthProvider(authProviderId);
var userAuthData = (Map<String, Object>) authInfo.getAuthData().get(authProviderId);
SMAutoAssign autoAssign = getAutoAssignUserData(authId, authProvider, userAuthData, finishAuthMonitor);
if (autoAssign != null) {
detectedAuthRole = autoAssign.getAuthRole();
}
var userIdFromCreds = findOrCreateExternalUserByCredentials(
authProviderId,
authProvider,
authAttemptSessionInfo.getSessionParams(),
userAuthData,
finishAuthMonitor,
activeUserId,
activeUserId == null,
authInfo.getAuthRole()
detectedAuthRole
);
if (userIdFromCreds == null) {
@@ -1322,6 +1364,14 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
updateAuthStatus(authId, SMAuthStatus.ERROR, storedUserData, error);
return SMAuthInfo.error(authId, error);
}
if (autoAssign != null && !CommonUtils.isEmpty(autoAssign.getExternalTeamIds())) {
if (allTeams == null) {
allTeams = readAllTeams();
}
autoUpdateUserTeams(authProvider, autoAssign, userIdFromCreds, allTeams);
}
if (activeUserId == null) {
activeUserId = userIdFromCreds;
}
@@ -1331,7 +1381,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
);
}
String tokenAuthRole = updateUserAuthRoleIfNeeded(activeUserId, authInfo.getAuthRole());
String tokenAuthRole = updateUserAuthRoleIfNeeded(activeUserId, detectedAuthRole);
if (smTokens == null && permissions == null) {
try (Connection dbCon = database.openConnection()) {
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
@@ -1368,10 +1418,101 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
//refresh token must be sent only once
isMainAuthSession ? smTokens.getSmRefreshToken() : null,
permissions,
authInfo.getAuthData()
authInfo.getAuthData(),
tokenAuthRole
);
}
private void autoUpdateUserTeams(
WebAuthProviderDescriptor authProvider,
SMAutoAssign autoAssign,
String userId,
SMTeam[] allTeams
) throws DBCException {
if (!(authProvider.getInstance() instanceof SMAuthProviderAssigner)) {
return;
}
SMAuthProviderAssigner authProviderAssigner = (SMAuthProviderAssigner) authProvider.getInstance();
String[] newTeamIds = autoAssign.getExternalTeamIds()
.stream()
.map(externalTeamId -> findTeamByExternalTeamId(
allTeams,
authProviderAssigner.getExternalTeamIdMetadataFieldName(),
externalTeamId
))
.filter(Objects::nonNull)
.map(SMTeam::getTeamId)
.toArray(String[]::new);
if (!ArrayUtils.isEmpty(newTeamIds)) {
setUserTeams(
userId,
newTeamIds,
userId
);
}
}
@Nullable
private SMAutoAssign getAutoAssignUserData(
String authAttemptId,
WebAuthProviderDescriptor authProvider,
Map<String, Object> userData,
DBRProgressMonitor monitor
) throws DBException {
var authProviderInstance = authProvider.getInstance();
if (!(authProviderInstance instanceof SMAuthProviderAssigner)) {
return null;
}
var authProviderAssigner = (SMAuthProviderAssigner) authProviderInstance;
String providerConfigId = readProviderConfigId(authAttemptId, authProvider.getId());
if (providerConfigId == null) {
return null;
}
var providerConfig =
application.getAuthConfiguration().getAuthProviderConfiguration(providerConfigId);
return authProviderAssigner.detectAutoAssignments(
monitor,
providerConfig == null ? Map.of() : providerConfig.getParameters(),
userData
);
}
@Nullable
private SMTeam findTeamByExternalTeamId(SMTeam[] allTeams, String externalGroupParameterName, String groupId) {
for (SMTeam team : allTeams) {
String teamGroupId = team.getMetaParameters().get(externalGroupParameterName);
if (CommonUtils.equalObjects(teamGroupId, groupId)) {
return team;
}
}
return null;
}
private String readProviderConfigId(String authAttemptId, String authProviderId) throws DBException {
try (Connection dbCon = database.openConnection()) {
try (PreparedStatement dbStat = dbCon.prepareStatement(
"SELECT AUTH_PROVIDER_CONFIGURATION_ID " +
"FROM CB_AUTH_ATTEMPT_INFO "
+ "WHERE AUTH_ID=? AND AUTH_PROVIDER_ID=?"
)) {
dbStat.setString(1, authAttemptId);
dbStat.setString(2, authProviderId);
try (ResultSet dbResult = dbStat.executeQuery()) {
if (!dbResult.next()) {
return null;
}
return dbResult.getString(1);
}
}
} catch (SQLException e) {
throw new DBException("Failed to read auth provider configuration", e);
}
}
@Nullable
protected String readUserAuthRole(String userId) throws DBException {
try (Connection dbCon = database.openConnection()) {
@@ -1430,7 +1571,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
private String findOrCreateExternalUserByCredentials(
@NotNull String authProviderId,
@NotNull WebAuthProviderDescriptor authProvider,
@NotNull Map<String, Object> sessionParameters,
@NotNull Map<String, Object> userCredentials,
@NotNull DBRProgressMonitor progressMonitor,
@@ -1438,9 +1579,8 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
boolean createNewUserIfNotExist,
String authRole
) throws DBException {
AuthProviderDescriptor authProvider = getAuthProvider(authProviderId);
SMAuthProvider<?> smAuthProviderInstance = authProvider.getInstance();
String userId = findUserByCredentials(authProviderId, userCredentials);
String userId = findUserByCredentials(authProvider, userCredentials);
String userIdFromCredentials;
try {
userIdFromCredentials = smAuthProviderInstance.validateLocalAuth(progressMonitor, this, Map.of(), userCredentials, null);
@@ -1450,7 +1590,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
if (activeUserId != null && userId != null && !activeUserId.equals(userId)) {
log.debug("User '" + activeUserId + "' is authenticated in '"
+ authProviderId + "' auth provider with credentials of user '"
+ authProvider.getId() + "' auth provider with credentials of user '"
+ userIdFromCredentials + "'");
}
if (userId == null && createNewUserIfNotExist) {
@@ -1470,7 +1610,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
setUserTeams(userId, new String[]{defaultTeamName}, userId);
}
}
setUserCredentials(userId, authProviderId, userCredentials);
setUserCredentials(userId, authProvider.getId(), userCredentials);
} else if (userId == null) {
userId = userIdFromCredentials;
}
@@ -1564,12 +1704,12 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
WebAuthConfiguration appConfiguration = (WebAuthConfiguration) application.getAppConfiguration();
Set<SMAuthProviderCustomConfiguration> customConfigurations = appConfiguration.getAuthCustomConfigurations();
List<SMAuthProviderDescriptor> providers = AuthProviderRegistry.getInstance().getAuthProviders().stream()
List<SMAuthProviderDescriptor> providers = WebAuthProviderRegistry.getInstance().getAuthProviders().stream()
.filter(ap ->
!ap.isTrusted() &&
appConfiguration.isAuthProviderEnabled(ap.getId()) &&
(!ap.isConfigurable() || hasProviderConfiguration(ap, customConfigurations)))
.map(AuthProviderDescriptor::createDescriptorBean).collect(Collectors.toList());
.map(WebAuthProviderDescriptor::createDescriptorBean).collect(Collectors.toList());
if (!CommonUtils.isEmpty(customConfigurations)) {
// Attach custom configs to providers
@@ -1587,7 +1727,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
return providers.toArray(new SMAuthProviderDescriptor[0]);
}
private static boolean hasProviderConfiguration(AuthProviderDescriptor ap, Set<SMAuthProviderCustomConfiguration> customConfigurations) {
private static boolean hasProviderConfiguration(WebAuthProviderDescriptor ap, Set<SMAuthProviderCustomConfiguration> customConfigurations) {
for (SMAuthProviderCustomConfiguration cc : customConfigurations) {
if (!cc.isDisabled() && cc.getProvider().equals(ap.getId())) {
return true;
@@ -1895,7 +2035,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
try (PreparedStatement dbStat = dbCon.prepareStatement(
"INSERT INTO CB_AUTH_PROVIDER(PROVIDER_ID,IS_ENABLED) VALUES(?,'Y')")) {
for (AuthProviderDescriptor authProvider : AuthProviderRegistry.getInstance().getAuthProviders()) {
for (WebAuthProviderDescriptor authProvider : WebAuthProviderRegistry.getInstance().getAuthProviders()) {
if (!registeredProviders.contains(authProvider.getId())) {
dbStat.setString(1, authProvider.getId());
dbStat.executeUpdate();
@@ -1925,8 +2065,8 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
}
private AuthProviderDescriptor getAuthProvider(String authProviderId) throws DBCException {
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(authProviderId);
private WebAuthProviderDescriptor getAuthProvider(String authProviderId) throws DBCException {
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(authProviderId);
if (authProvider == null) {
throw new DBCException("Auth provider not found: " + authProviderId);
}
@@ -21,6 +21,8 @@ import com.google.gson.GsonBuilder;
import io.cloudbeaver.auth.provider.local.LocalAuthProviderConstants;
import io.cloudbeaver.model.app.WebApplication;
import io.cloudbeaver.model.session.WebAuthInfo;
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
import io.cloudbeaver.registry.WebAuthProviderRegistry;
import io.cloudbeaver.utils.WebAppUtils;
import org.apache.commons.dbcp2.*;
import org.apache.commons.pool2.impl.GenericObjectPool;
@@ -42,8 +44,6 @@ import org.jkiss.dbeaver.model.sql.schema.ClassLoaderScriptSource;
import org.jkiss.dbeaver.model.sql.schema.SQLSchemaManager;
import org.jkiss.dbeaver.model.sql.schema.SQLSchemaVersionManager;
import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
import org.jkiss.dbeaver.registry.auth.AuthProviderDescriptor;
import org.jkiss.dbeaver.registry.auth.AuthProviderRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.dbeaver.utils.RuntimeUtils;
@@ -68,7 +68,7 @@ public class CBDatabase {
public static final String SCHEMA_UPDATE_SQL_PATH = "db/cb_schema_update_";
private static final int LEGACY_SCHEMA_VERSION = 1;
private static final int CURRENT_SCHEMA_VERSION = 11;
private static final int CURRENT_SCHEMA_VERSION = 12;
private static final String DEFAULT_DB_USER_NAME = "cb-data";
private static final String DEFAULT_DB_PWD_FILE = ".database-credentials.dat";
@@ -219,7 +219,7 @@ public class CBDatabase {
// Associate all auth credentials with admin user
for (WebAuthInfo ai : authInfoList) {
if (!ai.getAuthProvider().equals(LocalAuthProviderConstants.PROVIDER_ID)) {
AuthProviderDescriptor authProvider = ai.getAuthProviderDescriptor();
WebAuthProviderDescriptor authProvider = ai.getAuthProviderDescriptor();
Map<String, Object> userCredentials = ai.getUserCredentials();
if (!CommonUtils.isEmpty(userCredentials)) {
adminSecurityController.setUserCredentials(adminName, authProvider.getId(), userCredentials);
@@ -266,7 +266,7 @@ public class CBDatabase {
credentials.put(LocalAuthProviderConstants.CRED_USER, adminUser.getUserId());
credentials.put(LocalAuthProviderConstants.CRED_PASSWORD, clientPassword);
AuthProviderDescriptor authProvider = AuthProviderRegistry.getInstance().getAuthProvider(LocalAuthProviderConstants.PROVIDER_ID);
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(LocalAuthProviderConstants.PROVIDER_ID);
if (authProvider != null) {
adminSecurityController.setUserCredentials(adminUser.getUserId(), authProvider.getId(), credentials);
}
@@ -3,13 +3,13 @@
adminPassword: "test",
teams: [
{
teamId: "admin",
subjectId: "admin",
name: "Admin",
description: "Administrative access. Has all permissions.",
permissions: [ "public", "admin" ]
},
{
teamId: "user",
subjectId: "user",
name: "User",
description: "Standard user",
permissions: [ "public" ]