diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/config/CBAppConfig.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/config/CBAppConfig.java index 5f73d3ebeee..42147c6d1ba 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/config/CBAppConfig.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/config/CBAppConfig.java @@ -276,13 +276,21 @@ public void setDefaultAuthProvider(String defaultAuthProvider) { } @Override + @Deprecated public String[] getEnabledAuthProviders() { - if (enabledAuthProviders == null) { - // No config - enable all providers (+backward compatibility) - return WebAuthProviderRegistry.getInstance().getAuthProviders() - .stream().map(WebAuthProviderDescriptor::getId).toArray(String[]::new); - } - return enabledAuthProviders; + //all enabled + return WebAuthProviderRegistry.getInstance().getAuthProviders() + .stream() + .map(WebAuthProviderDescriptor::getId) + .toArray(String[]::new); +// if (enabledAuthProviders == null) { +// // No config - enable all providers (+backward compatibility) +// return WebAuthProviderRegistry.getInstance().getAuthProviders() +// .stream() +// .map(WebAuthProviderDescriptor::getId) +// .toArray(String[]::new); +// } +// return enabledAuthProviders; } public void setEnabledAuthProviders(String[] enabledAuthProviders) { diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java index 3b12df2ecb2..9ff68d9586b 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderConfiguration.java @@ -19,7 +19,6 @@ import io.cloudbeaver.auth.CBAuthConstants; import io.cloudbeaver.auth.SMAuthProviderFederated; import io.cloudbeaver.auth.SMSignOutLinkProvider; -import io.cloudbeaver.utils.ServletAppUtils; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; @@ -69,6 +68,10 @@ public boolean isDisabled() { return config.isDisabled(); } + public boolean isDefault() { + return config.isDefault(); + } + public String getIconURL() { return config.getIconURL(); } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java index 6156d3b5de0..6f307695aef 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderDescriptor.java @@ -29,6 +29,7 @@ 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.SMAuthProviderCustomConfiguration; import org.jkiss.dbeaver.model.security.SMAuthProviderDescriptor; import org.jkiss.dbeaver.model.security.SMSubjectType; import org.jkiss.utils.ArrayUtils; @@ -61,6 +62,8 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor { private final String[] requiredFeatures; private final boolean isRequired; private String[] types; + @Nullable + private final SMAuthProviderCustomConfiguration defaultConfiguration; public WebAuthProviderDescriptor(IConfigurationElement cfg) { super(cfg); @@ -98,6 +101,26 @@ public WebAuthProviderDescriptor(IConfigurationElement cfg) { String typesAttr = cfg.getAttribute(WebRegistryConstant.ATTR_CATEGORIES); this.types = CommonUtils.isEmpty(typesAttr) ? new String[0] : typesAttr.split(","); + IConfigurationElement[] defaultConfigurations = cfg.getChildren(WebRegistryConstant.TAG_DEFAULT_CONFIGURATION); + if (defaultConfigurations != null && defaultConfigurations.length > 0) { + if (defaultConfigurations.length > 1) { + throw new IllegalStateException("Multiple default configuration elements defined for auth provider " + getId()); + } + IConfigurationElement defaultConfiguration = defaultConfigurations[0]; + + this.defaultConfiguration = SMAuthProviderCustomConfiguration.builder() + .id(defaultConfiguration.getAttribute(WebRegistryConstant.ATTR_ID)) + .provider(getId()) + .displayName(defaultConfiguration.getAttribute(WebRegistryConstant.ATTR_LABEL)) + .disabled(CommonUtils.toBoolean(defaultConfiguration.getAttribute(WebRegistryConstant.ATTR_DISABLED))) + .iconURL(defaultConfiguration.getAttribute(WebRegistryConstant.ATTR_ICON)) + .description(defaultConfiguration.getAttribute(WebRegistryConstant.ATTR_DESCRIPTION)) + .parameters(new HashMap<>()) + .isDefault(CommonUtils.toBoolean(defaultConfiguration.getAttribute(WebRegistryConstant.ATTR_IS_DEFAULT))) + .build(); + } else { + this.defaultConfiguration = null; + } } @NotNull @@ -239,4 +262,9 @@ public boolean isFederated() { return false; } } + + @Nullable + public SMAuthProviderCustomConfiguration getDefaultConfiguration() { + return defaultConfiguration; + } } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderRegistry.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderRegistry.java index 767b600a16c..60fdebd8d83 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderRegistry.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebAuthProviderRegistry.java @@ -21,11 +21,13 @@ import org.eclipse.core.runtime.Platform; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.impl.PropertyDescriptor; +import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration; import org.jkiss.dbeaver.registry.RegistryConstants; import org.jkiss.utils.ArrayUtils; import org.jkiss.utils.CommonUtils; import java.util.*; +import java.util.stream.Collectors; public class WebAuthProviderRegistry { @@ -120,4 +122,11 @@ public WebAuthProviderDescriptor getAuthProvider(String id) { return authProviders.get(id); } + public Set getMandatoryConfigurations() { + return authProviders.values() + .stream() + .map(WebAuthProviderDescriptor::getDefaultConfiguration) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebRegistryConstant.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebRegistryConstant.java index 31c06abb49f..2a298fed20c 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebRegistryConstant.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/registry/WebRegistryConstant.java @@ -32,8 +32,11 @@ public class WebRegistryConstant { public static final String ATTR_REQUIRED_FEATURES = "requiredFeatures"; public static final String ATTR_CATEGORIES = "categories"; public static final String ATTR_SERVICE_PROVIDER = "serviceProvider"; + public static final String ATTR_DISABLED = "disabled"; + public static final String ATTR_IS_DEFAULT = "isDefault"; public static final String TAG_CONFIGURATION = "configuration"; public static final String TAG_CREDENTIALS = "credentials"; public static final String TAG_META_PARAMETERS = "metaParameters"; + public static final String TAG_DEFAULT_CONFIGURATION = "defaultConfiguration"; } diff --git a/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBServerConfigurationController.java b/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBServerConfigurationController.java index 338680eff3c..32578cb6288 100644 --- a/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBServerConfigurationController.java +++ b/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/CBServerConfigurationController.java @@ -24,6 +24,7 @@ import io.cloudbeaver.model.config.CBServerConfig; import io.cloudbeaver.model.config.PasswordPolicyConfiguration; import io.cloudbeaver.model.config.SMControllerConfiguration; +import io.cloudbeaver.registry.WebAuthProviderRegistry; import io.cloudbeaver.utils.ServletAppUtils; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; @@ -223,6 +224,8 @@ protected void mergeOldConfiguration(CBAppConfig prevConfig) { appConfiguration.getAuthCustomConfigurations().stream() ) .collect(Collectors.toCollection(LinkedHashSet::new)); + Set mandatoryConfigurations = WebAuthProviderRegistry.getInstance().getMandatoryConfigurations(); + mergedAuthProviders.addAll(mandatoryConfigurations); appConfiguration.setAuthProvidersConfigurations(mergedAuthProviders); } diff --git a/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/servlets/CBStaticServlet.java b/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/servlets/CBStaticServlet.java index 7d5d1678078..9cbcece18d6 100644 --- a/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/servlets/CBStaticServlet.java +++ b/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/server/servlets/CBStaticServlet.java @@ -134,6 +134,7 @@ private boolean performAutoLogin(HttpServletRequest request, HttpServletResponse } CBAppConfig appConfig = application.getAppConfiguration(); String[] authProviders = appConfig.getEnabledAuthProviders(); + //fixme there is no single auth provider if (authProviders.length == 1) { String authProviderId = authProviders[0]; WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(authProviderId); diff --git a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls index 1b1987cd79c..326c13f681e 100644 --- a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls +++ b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls @@ -83,6 +83,7 @@ type AdminAuthProviderConfiguration { id: ID! displayName: String! disabled: Boolean! + isDefault: Boolean! iconURL: String description: String diff --git a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls index 24960ec2de7..77756d2de7c 100644 --- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls +++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls @@ -31,6 +31,7 @@ type AuthProviderConfiguration { displayName: String! disabled: Boolean! authRoleProvided: Boolean + isDefault: Boolean iconURL: String description: String diff --git a/server/bundles/io.cloudbeaver.service.security/plugin.xml b/server/bundles/io.cloudbeaver.service.security/plugin.xml index 2fefe21d116..228cde97997 100644 --- a/server/bundles/io.cloudbeaver.service.security/plugin.xml +++ b/server/bundles/io.cloudbeaver.service.security/plugin.xml @@ -14,6 +14,12 @@ + { + async saveConfiguration(config: SaveAuthProviderConfigurationQueryVariables): Promise { await this.performUpdate(config.id, [], async () => { const response = await this.graphQLService.sdk.saveAuthProviderConfiguration(config); diff --git a/webapp/packages/core-sdk/src/queries/authentication/getAuthProviderConfigurations.gql b/webapp/packages/core-sdk/src/queries/authentication/getAuthProviderConfigurations.gql index 92b46590709..4c3df20b57f 100644 --- a/webapp/packages/core-sdk/src/queries/authentication/getAuthProviderConfigurations.gql +++ b/webapp/packages/core-sdk/src/queries/authentication/getAuthProviderConfigurations.gql @@ -1,17 +1,5 @@ query getAuthProviderConfigurations($providerId: ID) { configurations: listAuthProviderConfigurations(providerId: $providerId) { - providerId - id - displayName - disabled - iconURL - description - parameters - signInLink - signOutLink - redirectLink - metadataLink - acsLink - entityIdLink + ...AdminAuthProviderConfigurationInfo } } diff --git a/webapp/packages/core-sdk/src/queries/authentication/saveAuthProviderConfiguration.gql b/webapp/packages/core-sdk/src/queries/authentication/saveAuthProviderConfiguration.gql index fefbd1560bc..c7f3148bfc7 100644 --- a/webapp/packages/core-sdk/src/queries/authentication/saveAuthProviderConfiguration.gql +++ b/webapp/packages/core-sdk/src/queries/authentication/saveAuthProviderConfiguration.gql @@ -16,18 +16,6 @@ query saveAuthProviderConfiguration( description: $description parameters: $parameters ) { - providerId - id - displayName - disabled - iconURL - description - parameters - signInLink - signOutLink - redirectLink - metadataLink - acsLink - entityIdLink + ...AdminAuthProviderConfigurationInfo } } diff --git a/webapp/packages/core-sdk/src/queries/fragments/AdminAuthProviderConfigurationInfo.gql b/webapp/packages/core-sdk/src/queries/fragments/AdminAuthProviderConfigurationInfo.gql new file mode 100644 index 00000000000..354eddf1d07 --- /dev/null +++ b/webapp/packages/core-sdk/src/queries/fragments/AdminAuthProviderConfigurationInfo.gql @@ -0,0 +1,16 @@ +fragment AdminAuthProviderConfigurationInfo on AdminAuthProviderConfiguration { + providerId + id + displayName + disabled + isDefault + iconURL + description + parameters + signInLink + signOutLink + redirectLink + metadataLink + acsLink + entityIdLink +} diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/ServerConfiguration/AnonymousAccess.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/ServerConfiguration/AnonymousAccess.tsx new file mode 100644 index 00000000000..866aed8a58b --- /dev/null +++ b/webapp/packages/plugin-authentication-administration/src/Administration/ServerConfiguration/AnonymousAccess.tsx @@ -0,0 +1,63 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2025 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { observer } from 'mobx-react-lite'; +import { useContext } from 'react'; + +import { AUTH_PROVIDER_LOCAL_ID, AuthProvidersResource, AuthSettingsService } from '@cloudbeaver/core-authentication'; +import { FormContext, type PlaceholderComponent, Switch, useExecutor, useResource, useTranslate } from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; +import { CachedMapAllKey } from '@cloudbeaver/core-resource'; +import type { IConfigurationPlaceholderProps } from '@cloudbeaver/plugin-administration'; + +export const AnonymousAccess: PlaceholderComponent = observer(function AuthenticationProviders({ + state: { serverConfig }, +}) { + const providers = useResource(AuthenticationProviders, AuthProvidersResource, CachedMapAllKey); + const translate = useTranslate(); + const formContext = useContext(FormContext); + const authSettingsService = useService(AuthSettingsService); + + if (formContext === null) { + throw new Error('Form state should be provided'); + } + + const localProvider = providers.resource.get(AUTH_PROVIDER_LOCAL_ID); + const authenticationDisabled = serverConfig.enabledAuthProviders?.length === 0; + const isAnonymousAccessDisabled = authSettingsService.disableAnonymousAccess; + + useExecutor({ + executor: formContext.onChange, + handlers: [ + function switchControls() { + if (serverConfig.enabledAuthProviders?.length === 0) { + if (localProvider && !isAnonymousAccessDisabled) { + serverConfig.anonymousAccessEnabled = true; + } + } + }, + ], + }); + + if (!localProvider || isAnonymousAccessDisabled) { + return null; + } + + return ( + + {translate('administration_configuration_wizard_configuration_anonymous_access')} + + ); +}); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/ServerConfiguration/AuthenticationProviders.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/ServerConfiguration/AuthenticationProviders.tsx deleted file mode 100644 index d093630335a..00000000000 --- a/webapp/packages/plugin-authentication-administration/src/Administration/ServerConfiguration/AuthenticationProviders.tsx +++ /dev/null @@ -1,142 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2025 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0. - * you may not use this file except in compliance with the License. - */ -import { observer } from 'mobx-react-lite'; -import React, { useContext } from 'react'; - -import { - AUTH_PROVIDER_LOCAL_ID, - AuthProviderService, - AuthProvidersResource, - AuthSettingsService, - sortProvider, -} from '@cloudbeaver/core-authentication'; -import { FormContext, Group, GroupTitle, type PlaceholderComponent, Switch, useExecutor, useResource, useTranslate } from '@cloudbeaver/core-blocks'; -import { useService } from '@cloudbeaver/core-di'; -import { CachedMapAllKey } from '@cloudbeaver/core-resource'; -import { isDefined } from '@dbeaver/js-helpers'; -import type { IConfigurationPlaceholderProps } from '@cloudbeaver/plugin-administration'; - -import { ServerConfigurationAdminForm } from './ServerConfigurationAdminForm.js'; - -export const AuthenticationProviders: PlaceholderComponent = observer(function AuthenticationProviders({ - state: { serverConfig }, - configurationWizard, -}) { - const authProviderService = useService(AuthProviderService); - const providers = useResource(AuthenticationProviders, AuthProvidersResource, CachedMapAllKey); - const translate = useTranslate(); - const formContext = useContext(FormContext); - const authSettingsService = useService(AuthSettingsService); - - if (formContext === null) { - throw new Error('Form state should be provided'); - } - - const localProvider = providers.resource.get(AUTH_PROVIDER_LOCAL_ID); - const providerList = providers.data - .filter(isDefined) - .filter(provider => { - if (provider.private) { - return false; - } - - if (configurationWizard) { - const disabledByFeature = provider.requiredFeatures.some(feat => !serverConfig.enabledFeatures?.includes(feat)); - - if (provider.configurable || disabledByFeature || provider.authHidden) { - return false; - } - } - - return true; - }) - .sort(sortProvider); - - const externalAuthentication = providerList.length === 0; - const authenticationDisabled = serverConfig.enabledAuthProviders?.length === 0; - const isAnonymousAccessDisabled = authSettingsService.disableAnonymousAccess; - - useExecutor({ - executor: formContext.onChange, - handlers: [ - function switchControls() { - if (serverConfig.enabledAuthProviders?.length === 0) { - if (localProvider && !isAnonymousAccessDisabled) { - serverConfig.anonymousAccessEnabled = true; - } - } - - if (serverConfig.enabledAuthProviders?.length) { - serverConfig.enabledAuthProviders = serverConfig.enabledAuthProviders.filter(providerId => { - const provider = providers.resource.get(providerId)!; - - return !provider.requiredFeatures.some(feat => !serverConfig.enabledFeatures?.includes(feat)); - }); - } - }, - ], - }); - - if (externalAuthentication) { - return null; - } - - return ( - - - {translate('administration_configuration_wizard_configuration_authentication_group')} - {localProvider && !isAnonymousAccessDisabled ? ( - - {translate('administration_configuration_wizard_configuration_anonymous_access')} - - ) : null} - - {providerList.map(provider => { - const links = authProviderService.getServiceDescriptionLinks(provider); - const disabled = provider.requiredFeatures.some(feat => !serverConfig.enabledFeatures?.includes(feat)); - const tooltip = disabled ? `Following services need to be enabled: "${provider.requiredFeatures.join(', ')}"` : ''; - - return ( - - {provider.description} - {links.map(link => { - const Description = link.description(); - return ; - })} - - } - mod={['primary']} - disabled={disabled} - small - autoHide - > - {provider.label} - - ); - })} - - {configurationWizard && localProvider && } - - ); -}); diff --git a/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts b/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts index 45067c7bf3f..51007cd78b2 100644 --- a/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts +++ b/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts @@ -11,9 +11,7 @@ import { ServerConfigurationAdministrationNavService, ServerConfigurationService import { AuthenticationService } from '@cloudbeaver/plugin-authentication'; import { importLazyComponent } from '@cloudbeaver/core-blocks'; -const AuthenticationProviders = importLazyComponent(() => - import('./Administration/ServerConfiguration/AuthenticationProviders.js').then(m => m.AuthenticationProviders), -); +const AnonymousAccess = importLazyComponent(() => import('./Administration/ServerConfiguration/AnonymousAccess.js').then(m => m.AnonymousAccess)); @injectable(() => [ServerConfigurationService, ServerConfigurationAdministrationNavService, AuthenticationService]) export class PluginBootstrap extends Bootstrap { @@ -26,7 +24,7 @@ export class PluginBootstrap extends Bootstrap { } override register(): void { - this.serverConfigurationService.configurationContainer.add(AuthenticationProviders, 0); + this.serverConfigurationService.securitySettingsContainer.add(AnonymousAccess, 0); this.authenticationService.setConfigureAuthProvider(() => this.serverConfigurationAdministrationNavService.navToSettings()); } }