mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-4173 configurable iam provider (#2126)
* CB-4174 configurable iam provider * CB-4174 refactor: display only available auth providers * CB-4174 refactor: display only available auth providers * CB-4174 refactor: display only available auth providers * CB-4174 chore: remove unused config properties --------- Co-authored-by: Aleksei Potsetsuev <wrouds@gmail.com> Co-authored-by: Evgenia Bezborodova <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
co-authored by
Aleksei Potsetsuev
Evgenia Bezborodova
parent
c6e8d27e31
commit
dfd572aa4c
@@ -16,9 +16,6 @@
|
||||
maxLogRecords: 2000,
|
||||
maxFailedRequests: 3
|
||||
}
|
||||
},
|
||||
authentication: {
|
||||
primaryAuthProvider: 'local'
|
||||
}
|
||||
},
|
||||
// Notifications config
|
||||
|
||||
+3
@@ -18,6 +18,7 @@
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import io.cloudbeaver.auth.CBAuthConstants;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
|
||||
public interface WebAuthApplication extends WebApplication {
|
||||
WebAuthConfiguration getAuthConfiguration();
|
||||
@@ -27,4 +28,6 @@ public interface WebAuthApplication extends WebApplication {
|
||||
default long getMaxSessionIdleTime() {
|
||||
return CBAuthConstants.MAX_SESSION_IDLE_TIME;
|
||||
}
|
||||
|
||||
void flushConfiguration() throws DBException;
|
||||
}
|
||||
|
||||
+3
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
|
||||
@@ -36,4 +37,6 @@ public interface WebAuthConfiguration {
|
||||
|
||||
@Nullable
|
||||
SMAuthProviderCustomConfiguration getAuthProviderConfiguration(String configId);
|
||||
|
||||
void addAuthProviderConfiguration(@NotNull SMAuthProviderCustomConfiguration config);
|
||||
}
|
||||
|
||||
+6
-2
@@ -17,11 +17,11 @@
|
||||
package io.cloudbeaver.service;
|
||||
|
||||
import io.cloudbeaver.model.app.WebAppConfiguration;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.app.DBPApplication;
|
||||
|
||||
/**
|
||||
* Web service implementation
|
||||
@@ -29,11 +29,15 @@ import org.jkiss.dbeaver.model.app.DBPApplication;
|
||||
public interface DBWServiceServerConfigurator extends DBWServiceBinding {
|
||||
|
||||
void configureServer(
|
||||
@NotNull DBPApplication application,
|
||||
@NotNull WebApplication application,
|
||||
@Nullable WebSession session,
|
||||
@NotNull WebAppConfiguration appConfig
|
||||
) throws DBException;
|
||||
|
||||
default void migrateConfigurationIfNeeded(@NotNull WebApplication application) {
|
||||
|
||||
}
|
||||
|
||||
void reloadConfiguration(@NotNull WebAppConfiguration appConfig) throws DBException;
|
||||
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import org.jkiss.dbeaver.model.auth.AuthInfo;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.InstanceCreator;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.auth.CBAuthConstants;
|
||||
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
|
||||
import io.cloudbeaver.model.app.BaseWebApplication;
|
||||
import io.cloudbeaver.model.app.WebAuthApplication;
|
||||
import io.cloudbeaver.model.app.WebAuthConfiguration;
|
||||
@@ -43,6 +43,7 @@ import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.ModelPreferences;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.app.DBPApplication;
|
||||
import org.jkiss.dbeaver.model.auth.AuthInfo;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
|
||||
@@ -430,7 +431,14 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
|
||||
protected void initializeServer() throws DBException {
|
||||
|
||||
for (DBWServiceServerConfigurator wsc : WebServiceRegistry.getInstance()
|
||||
.getWebServices(DBWServiceServerConfigurator.class)) {
|
||||
try {
|
||||
wsc.migrateConfigurationIfNeeded(this);
|
||||
} catch (Exception e) {
|
||||
log.warn("Error migration configuration " + wsc.getClass().getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void determineLocalAddresses() {
|
||||
@@ -889,8 +897,12 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
@NotNull List<AuthInfo> authInfoList
|
||||
) throws DBException;
|
||||
|
||||
public synchronized void flushConfiguration(SMCredentialsProvider credentialsProvider) throws DBException {
|
||||
saveRuntimeConfig(serverName, serverURL, maxSessionIdleTime, appConfiguration, credentialsProvider);
|
||||
public synchronized void flushConfiguration(SMCredentialsProvider webSession) throws DBException {
|
||||
saveRuntimeConfig(serverName, serverURL, maxSessionIdleTime, appConfiguration, webSession);
|
||||
}
|
||||
|
||||
public synchronized void flushConfiguration() throws DBException {
|
||||
saveRuntimeConfig(serverName, serverURL, maxSessionIdleTime, appConfiguration, new NoAuthCredentialsProvider());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
# max size of the file that can be uploaded to the editor (in kilobytes)
|
||||
maxFileSize: 100
|
||||
}
|
||||
},
|
||||
authentication: {
|
||||
primaryAuthProvider: 'local'
|
||||
}
|
||||
},
|
||||
// Notifications config
|
||||
|
||||
@@ -67,26 +67,10 @@ export class AuthProvidersResource extends CachedMapResource<string, AuthProvide
|
||||
return this.get(resourceKeyList(this.serverConfigResource.enabledAuthProviders)) as AuthProvider[];
|
||||
}
|
||||
|
||||
getBase(): string | undefined {
|
||||
return this.authSettingsService.settings.getValue('baseAuthProvider');
|
||||
}
|
||||
|
||||
getPrimary(): string {
|
||||
return this.authSettingsService.settings.getValue('primaryAuthProvider');
|
||||
}
|
||||
|
||||
isEnabled(id: string): boolean {
|
||||
return this.isAuthEnabled(id);
|
||||
}
|
||||
|
||||
isBase(id: string): boolean {
|
||||
return id === this.getBase();
|
||||
}
|
||||
|
||||
isPrimary(id: string): boolean {
|
||||
return id === this.getPrimary();
|
||||
}
|
||||
|
||||
isAuthEnabled(id: string): boolean {
|
||||
return this.serverConfigResource.enabledAuthProviders.includes(id);
|
||||
}
|
||||
|
||||
@@ -49,8 +49,6 @@ beforeAll(() => app.init());
|
||||
const equalConfig = {
|
||||
core: {
|
||||
authentication: {
|
||||
baseAuthProvider: 'sd',
|
||||
primaryAuthProvider: 'sd',
|
||||
disableAnonymousAccess: true,
|
||||
} as AuthSettings,
|
||||
},
|
||||
@@ -64,7 +62,5 @@ test('Read settings', async () => {
|
||||
|
||||
await config.refresh();
|
||||
|
||||
expect(settings.settings.getValue('baseAuthProvider')).toBe('sd');
|
||||
expect(settings.settings.getValue('primaryAuthProvider')).toBe('sd');
|
||||
expect(settings.settings.getValue('disableAnonymousAccess')).toBe(true);
|
||||
});
|
||||
|
||||
@@ -12,8 +12,6 @@ import { SettingsManagerService } from '@cloudbeaver/core-settings';
|
||||
import { AUTH_SETTINGS_GROUP, settings } from './AUTH_SETTINGS_GROUP';
|
||||
|
||||
const defaultSettings = {
|
||||
baseAuthProvider: undefined as undefined | string,
|
||||
primaryAuthProvider: 'local',
|
||||
disableAnonymousAccess: false,
|
||||
};
|
||||
|
||||
|
||||
@@ -23,9 +23,6 @@ export const defaultProductConfiguration: Record<string, any> = {
|
||||
maxFailedRequests: 3.0,
|
||||
},
|
||||
},
|
||||
authentication: {
|
||||
primaryAuthProvider: 'local',
|
||||
},
|
||||
},
|
||||
plugin_data_export: {
|
||||
disabled: false,
|
||||
|
||||
+15
-28
@@ -8,16 +8,11 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import {
|
||||
AUTH_PROVIDER_LOCAL_ID,
|
||||
AuthProvider,
|
||||
AuthProviderService,
|
||||
AuthProvidersResource,
|
||||
AuthSettingsService,
|
||||
} from '@cloudbeaver/core-authentication';
|
||||
import { AUTH_PROVIDER_LOCAL_ID, AuthProviderService, AuthProvidersResource, AuthSettingsService } from '@cloudbeaver/core-authentication';
|
||||
import { FormContext, Group, GroupTitle, PlaceholderComponent, Switch, useExecutor, useResource, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { CachedMapAllKey } from '@cloudbeaver/core-resource';
|
||||
import { isDefined } from '@cloudbeaver/core-utils';
|
||||
import type { IConfigurationPlaceholderProps } from '@cloudbeaver/plugin-administration';
|
||||
|
||||
import { ServerConfigurationAdminForm } from './ServerConfigurationAdminForm';
|
||||
@@ -36,17 +31,24 @@ export const AuthenticationProviders: PlaceholderComponent<IConfigurationPlaceho
|
||||
throw new Error('Form state should be provided');
|
||||
}
|
||||
|
||||
const providerList = providers.data.filter<AuthProvider>((provider): provider is AuthProvider => {
|
||||
if (configurationWizard && (provider?.configurable || provider?.private)) {
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
const localProvider = providers.resource.get(AUTH_PROVIDER_LOCAL_ID);
|
||||
const primaryProvider = providers.resource.get(providers.resource.getPrimary());
|
||||
const externalAuthentication = localProvider === undefined && providerList.length === 1;
|
||||
const externalAuthentication = providerList.length === 0;
|
||||
const authenticationDisabled = serverConfig.enabledAuthProviders?.length === 0;
|
||||
const isAnonymousAccessDisabled = authSettingsService.settings.getValue('disableAnonymousAccess');
|
||||
|
||||
@@ -57,8 +59,6 @@ export const AuthenticationProviders: PlaceholderComponent<IConfigurationPlaceho
|
||||
if (serverConfig.enabledAuthProviders?.length === 0) {
|
||||
if (localProvider && !isAnonymousAccessDisabled) {
|
||||
serverConfig.anonymousAccessEnabled = true;
|
||||
} else if (primaryProvider) {
|
||||
serverConfig.enabledAuthProviders.push(primaryProvider.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,22 +97,9 @@ export const AuthenticationProviders: PlaceholderComponent<IConfigurationPlaceho
|
||||
|
||||
{providerList.map(provider => {
|
||||
const links = authProviderService.getServiceDescriptionLinks(provider);
|
||||
let disabled = provider.requiredFeatures.some(feat => !serverConfig.enabledFeatures?.includes(feat));
|
||||
const disabled = provider.requiredFeatures.some(feat => !serverConfig.enabledFeatures?.includes(feat));
|
||||
const tooltip = disabled ? `Following services need to be enabled: "${provider.requiredFeatures.join(', ')}"` : '';
|
||||
|
||||
if (
|
||||
!localProvider &&
|
||||
primaryProvider?.id === provider.id &&
|
||||
serverConfig.enabledAuthProviders?.length === 1 &&
|
||||
serverConfig.enabledAuthProviders.includes(provider.id)
|
||||
) {
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
if (provider.private || (configurationWizard && (disabled || provider.id !== AUTH_PROVIDER_LOCAL_ID))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Switch
|
||||
key={provider.id}
|
||||
|
||||
@@ -53,15 +53,10 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string |
|
||||
const authInfoService = useService(AuthInfoService);
|
||||
const notificationService = useService(NotificationService);
|
||||
|
||||
const primaryId = authProvidersResource.resource.getPrimary();
|
||||
const adminPageActive = administrationScreenService.isAdministrationPageActive;
|
||||
const providers = authProvidersResource.data.filter(notEmptyProvider).sort(compareProviders);
|
||||
|
||||
const activeProviders = providers.filter(provider => {
|
||||
if (provider.id === primaryId && adminPageActive && accessRequest) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (provider.federated || provider.trusted || provider.private) {
|
||||
return false;
|
||||
}
|
||||
@@ -170,9 +165,6 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string |
|
||||
|
||||
get configure(): boolean {
|
||||
if (state.activeProvider) {
|
||||
if (this.adminPageActive && authProvidersResource.resource.isPrimary(state.activeProvider.id)) {
|
||||
return false;
|
||||
}
|
||||
return !authProvidersResource.resource.isAuthEnabled(state.activeProvider.id);
|
||||
}
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user