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:
Alexander Skoblikov
2023-11-15 20:12:53 +03:00
committed by GitHub
co-authored by Aleksei Potsetsuev Evgenia Bezborodova
parent c6e8d27e31
commit dfd572aa4c
12 changed files with 43 additions and 73 deletions
-3
View File
@@ -16,9 +16,6 @@
maxLogRecords: 2000,
maxFailedRequests: 3
}
},
authentication: {
primaryAuthProvider: 'local'
}
},
// Notifications config
@@ -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;
}
@@ -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);
}
@@ -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,
@@ -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;