mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-3452 NTLM (#1919)
* CB-3837 separate flag for federated providers * CB-3452 feat: support configurable auth providers * CB-3452 fix: auth tab switching * CB-3452 fix: auth dialog credentials reset * CB-3837 separate flag for federated providers * CB-3452 feat: support configurable auth providers * CB-3452 fix: auth tab switching * CB-3452 fix: auth dialog credentials reset * CB-3836 ntlm auth provider * CB-3452 fix: provider configuration tab id * CB-3836 fix ntlm flow * CB-3452 chore: add Integer type mapping --------- Co-authored-by: Aleksei Potsetsuev <wrouds@gmail.com> Co-authored-by: EvgeniaBzzz <139753579+EvgeniaBzzz@users.noreply.github.com> Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
This commit is contained in:
co-authored by
Aleksei Potsetsuev
EvgeniaBzzz
Daria Marutkina
parent
aa1edf0e29
commit
35668e69b4
+2
-1
@@ -19,6 +19,7 @@ package io.cloudbeaver.auth;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -31,7 +32,7 @@ public interface SMAuthProviderAssigner {
|
||||
@NotNull
|
||||
SMAutoAssign detectAutoAssignments(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull Map<String, Object> providerConfig,
|
||||
@NotNull SMAuthProviderCustomConfiguration providerConfig,
|
||||
@NotNull Map<String, Object> authParameters
|
||||
) throws DBException;
|
||||
|
||||
|
||||
+5
@@ -17,6 +17,7 @@
|
||||
package io.cloudbeaver.model.user;
|
||||
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.auth.SMAuthProviderFederated;
|
||||
import io.cloudbeaver.auth.provisioning.SMProvisioner;
|
||||
import io.cloudbeaver.registry.WebAuthProviderConfiguration;
|
||||
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
|
||||
@@ -70,6 +71,10 @@ public class WebAuthProviderInfo {
|
||||
return descriptor.isConfigurable();
|
||||
}
|
||||
|
||||
public boolean isFederated() {
|
||||
return descriptor.getInstance() instanceof SMAuthProviderFederated;
|
||||
}
|
||||
|
||||
public boolean isTrusted() {
|
||||
return descriptor.isTrusted();
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@ type AuthProviderInfo {
|
||||
|
||||
# Configurable providers must be configured first. See configurations field.
|
||||
configurable: Boolean!
|
||||
# Federated providers means authorization must occur asynchronously through redirects.
|
||||
federated: Boolean!
|
||||
|
||||
# Provider configurations (applicable only if configurable=true)
|
||||
configurations: [AuthProviderConfiguration!]
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ public class LocalAuthProvider implements SMAuthProvider<LocalAuthSession> {
|
||||
@Override
|
||||
public String validateLocalAuth(@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull SMController securityController,
|
||||
@NotNull Map<String, Object> providerConfig,
|
||||
@NotNull SMAuthProviderCustomConfiguration providerConfig,
|
||||
@NotNull Map<String, Object> userCredentials,
|
||||
@Nullable String activeUserId) throws DBException {
|
||||
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ public class RPAuthProvider implements SMAuthProviderExternal<SMSession> {
|
||||
public String validateLocalAuth(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull SMController securityController,
|
||||
@NotNull Map<String, Object> providerConfig,
|
||||
@NotNull SMAuthProviderCustomConfiguration providerConfig,
|
||||
@NotNull Map<String, Object> userCredentials,
|
||||
@Nullable String activeUserId
|
||||
) throws DBException {
|
||||
|
||||
+5
-5
@@ -1285,7 +1285,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
SMAuthInfo.inProgress(
|
||||
authAttemptId,
|
||||
null,
|
||||
Map.of(new SMAuthConfigurationReference(authProviderId, null), securedUserIdentifyingCredentials)
|
||||
Map.of(new SMAuthConfigurationReference(authProviderId, authProviderConfigurationId), securedUserIdentifyingCredentials)
|
||||
),
|
||||
true,
|
||||
false
|
||||
@@ -1745,8 +1745,8 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
|
||||
Map<SMAuthConfigurationReference, Object> storedUserData = new LinkedHashMap<>();
|
||||
SMTeam[] allTeams = null;
|
||||
SMAuthProviderCustomConfiguration providerConfig = null;
|
||||
String detectedAuthRole = null;
|
||||
Map<String, Object> providerConfig = new LinkedHashMap<>();
|
||||
Map<String, Object> userAuthData = new LinkedHashMap<>();
|
||||
for (SMAuthConfigurationReference authConfiguration : authProviderIds) {
|
||||
String authProviderId = authConfiguration.getAuthProviderId();
|
||||
@@ -1761,7 +1761,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
var providerCustomConfig =
|
||||
application.getAuthConfiguration().getAuthProviderConfiguration(providerConfigId);
|
||||
if (providerCustomConfig != null) {
|
||||
providerConfig.putAll(providerCustomConfig.getParameters());
|
||||
providerConfig = providerCustomConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1893,7 +1893,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
@Nullable
|
||||
private SMAutoAssign getAutoAssignUserData(
|
||||
WebAuthProviderDescriptor authProvider,
|
||||
Map<String, Object> providerConfig,
|
||||
SMAuthProviderCustomConfiguration providerConfig,
|
||||
Map<String, Object> userData,
|
||||
DBRProgressMonitor monitor
|
||||
) throws DBException {
|
||||
@@ -2005,7 +2005,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
|
||||
@Nullable String activeUserId,
|
||||
boolean createNewUserIfNotExist,
|
||||
String authRole,
|
||||
Map<String, Object> providerConfig
|
||||
SMAuthProviderCustomConfiguration providerConfig
|
||||
) throws DBException {
|
||||
SMAuthProvider<?> smAuthProviderInstance = authProvider.getInstance();
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ fragment AuthProviderInfo on AuthProviderInfo {
|
||||
# features
|
||||
#}
|
||||
|
||||
federated
|
||||
configurable
|
||||
configurations {
|
||||
...AuthProviderConfigurationInfo
|
||||
|
||||
@@ -27,7 +27,7 @@ import { AuthDialogFooter } from './AuthDialogFooter';
|
||||
import { AuthProviderForm } from './AuthProviderForm/AuthProviderForm';
|
||||
import { ConfigurationsList } from './AuthProviderForm/ConfigurationsList';
|
||||
import { FEDERATED_AUTH } from './FEDERATED_AUTH';
|
||||
import { useAuthDialogState } from './useAuthDialogState';
|
||||
import { getAuthProviderTabId, useAuthDialogState } from './useAuthDialogState';
|
||||
|
||||
const styles = css`
|
||||
CommonDialogWrapper {
|
||||
@@ -87,7 +87,7 @@ export const AuthDialog: DialogComponent<IAuthOptions, null> = observer(function
|
||||
|
||||
const additional = userInfo.data !== null && state.activeProvider?.id !== undefined && !userInfo.hasToken(state.activeProvider.id);
|
||||
|
||||
const showTabs = dialogData.providers.length + dialogData.configurations.length > 1;
|
||||
const showTabs = dialogData.providers.length + dialogData.federatedProviders.length > 1;
|
||||
const federate = state.tabId === FEDERATED_AUTH;
|
||||
|
||||
let dialogTitle = translate('authentication_login_dialog_title');
|
||||
@@ -124,7 +124,7 @@ export const AuthDialog: DialogComponent<IAuthOptions, null> = observer(function
|
||||
authenticationService.configureAuthProvider?.();
|
||||
}
|
||||
|
||||
function renderForm(provider: AuthProvider | null) {
|
||||
function renderForm(provider: AuthProvider | null, configuration: AuthProviderConfiguration | null) {
|
||||
if (!provider) {
|
||||
return <TextPlaceholder>{translate('authentication_select_provider')}</TextPlaceholder>;
|
||||
}
|
||||
@@ -146,7 +146,9 @@ export const AuthDialog: DialogComponent<IAuthOptions, null> = observer(function
|
||||
);
|
||||
}
|
||||
|
||||
return <AuthProviderForm provider={provider} credentials={state.credentials} authenticate={dialogData.authenticating} />;
|
||||
return (
|
||||
<AuthProviderForm provider={provider} configuration={configuration} credentials={state.credentials} authenticate={dialogData.authenticating} />
|
||||
);
|
||||
}
|
||||
|
||||
return styled(useStyles(BASE_TAB_STYLES, styles, UNDERLINE_TAB_STYLES, UNDERLINE_TAB_BIG_STYLES))(
|
||||
@@ -161,27 +163,50 @@ export const AuthDialog: DialogComponent<IAuthOptions, null> = observer(function
|
||||
<CommonDialogBody noBodyPadding>
|
||||
{showTabs && (
|
||||
<TabList aria-label="Auth providers">
|
||||
{dialogData.providers.map(provider => (
|
||||
<Tab
|
||||
key={provider.id}
|
||||
tabId={provider.id}
|
||||
title={provider.description || provider.label}
|
||||
disabled={dialogData.authenticating}
|
||||
onClick={() => {
|
||||
state.setActiveProvider(provider);
|
||||
}}
|
||||
>
|
||||
<TabTitle>{provider.label}</TabTitle>
|
||||
</Tab>
|
||||
))}
|
||||
{dialogData.configurations.length > 0 && (
|
||||
{dialogData.providers
|
||||
.map(provider => {
|
||||
if (provider.configurable) {
|
||||
return provider.configurations?.map(configuration => {
|
||||
const tabId = getAuthProviderTabId(provider, configuration);
|
||||
return (
|
||||
<Tab
|
||||
key={tabId}
|
||||
tabId={tabId}
|
||||
title={configuration.displayName}
|
||||
disabled={dialogData.authenticating}
|
||||
onClick={() => {
|
||||
state.setActiveProvider(provider, configuration);
|
||||
}}
|
||||
>
|
||||
<TabTitle>{configuration.displayName}</TabTitle>
|
||||
</Tab>
|
||||
);
|
||||
});
|
||||
}
|
||||
return (
|
||||
<Tab
|
||||
key={provider.id}
|
||||
tabId={provider.id}
|
||||
title={provider.description || provider.label}
|
||||
disabled={dialogData.authenticating}
|
||||
onClick={() => {
|
||||
state.setActiveProvider(provider, null);
|
||||
}}
|
||||
>
|
||||
<TabTitle>{provider.label}</TabTitle>
|
||||
</Tab>
|
||||
);
|
||||
})
|
||||
.flat()}
|
||||
{dialogData.federatedProviders.length > 0 && (
|
||||
<Tab
|
||||
key={FEDERATED_AUTH}
|
||||
tabId={FEDERATED_AUTH}
|
||||
title={translate('authentication_auth_federated')}
|
||||
disabled={dialogData.authenticating}
|
||||
onClick={() => {
|
||||
state.setActiveProvider(null);
|
||||
state.setActiveProvider(null, null);
|
||||
state.setTabId(FEDERATED_AUTH);
|
||||
}}
|
||||
>
|
||||
<TabTitle>{translate('authentication_auth_federated')}</TabTitle>
|
||||
@@ -193,13 +218,13 @@ export const AuthDialog: DialogComponent<IAuthOptions, null> = observer(function
|
||||
<ConfigurationsList
|
||||
activeProvider={state.activeProvider}
|
||||
activeConfiguration={state.activeConfiguration}
|
||||
providers={dialogData.configurations}
|
||||
providers={dialogData.federatedProviders}
|
||||
authTask={dialogData.authTask}
|
||||
login={login}
|
||||
onClose={rejectDialog}
|
||||
/>
|
||||
) : (
|
||||
<SubmittingForm onSubmit={() => login(linkUser)}>{renderForm(state.activeProvider)}</SubmittingForm>
|
||||
<SubmittingForm onSubmit={() => login(linkUser)}>{renderForm(state.activeProvider, state.activeConfiguration)}</SubmittingForm>
|
||||
)}
|
||||
</CommonDialogBody>
|
||||
{!federate && (
|
||||
|
||||
+2
-1
@@ -7,11 +7,12 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
import type { AuthProvider, IAuthCredentials } from '@cloudbeaver/core-authentication';
|
||||
import type { AuthProvider, AuthProviderConfiguration, IAuthCredentials } from '@cloudbeaver/core-authentication';
|
||||
import { Combobox, Group, InputField, useFocus } from '@cloudbeaver/core-blocks';
|
||||
|
||||
interface Props {
|
||||
provider: AuthProvider;
|
||||
configuration: AuthProviderConfiguration | null;
|
||||
credentials: IAuthCredentials;
|
||||
authenticate: boolean;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ interface IData {
|
||||
configure: boolean;
|
||||
adminPageActive: boolean;
|
||||
providers: AuthProvider[];
|
||||
configurations: AuthProvider[];
|
||||
federatedProviders: AuthProvider[];
|
||||
|
||||
login: (linkUser: boolean, provider?: AuthProvider, configuration?: AuthProviderConfiguration) => Promise<void>;
|
||||
loginFederated: (provider: AuthProvider, configuration: AuthProviderConfiguration, onClose?: () => void) => Promise<void>;
|
||||
@@ -39,9 +39,8 @@ interface IState {
|
||||
activeConfiguration: AuthProviderConfiguration | null;
|
||||
credentials: IAuthCredentials;
|
||||
|
||||
setTabId: (tabId: string) => void;
|
||||
setActiveProvider: (provider: AuthProvider | null) => void;
|
||||
setActiveConfiguration: (provider: AuthProvider | null, configuration: AuthProviderConfiguration | null) => void;
|
||||
setTabId: (tabId: string | null) => void;
|
||||
setActiveProvider: (provider: AuthProvider | null, configuration: AuthProviderConfiguration | null) => void;
|
||||
}
|
||||
|
||||
export function useAuthDialogState(accessRequest: boolean, providerId: string | null, configurationId?: string): IData {
|
||||
@@ -64,18 +63,30 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string |
|
||||
credentials: {},
|
||||
},
|
||||
|
||||
setTabId(tabId: string): void {
|
||||
setTabId(tabId: string | null): void {
|
||||
this.tabId = tabId;
|
||||
},
|
||||
setActiveProvider(provider: AuthProvider | null): void {
|
||||
setActiveProvider(provider: AuthProvider | null, configuration: AuthProviderConfiguration | null): void {
|
||||
const providerChanged = this.activeProvider?.id !== provider?.id;
|
||||
const configurationChanged = this.activeConfiguration?.id !== configuration?.id;
|
||||
|
||||
this.activeProvider = provider;
|
||||
this.credentials.profile = '0';
|
||||
this.credentials.credentials = {};
|
||||
this.activeConfiguration = null;
|
||||
},
|
||||
setActiveConfiguration(provider: AuthProvider | null, configuration: AuthProviderConfiguration | null): void {
|
||||
this.setActiveProvider(provider);
|
||||
this.activeConfiguration = configuration;
|
||||
|
||||
if (providerChanged || configurationChanged) {
|
||||
this.credentials.profile = '0';
|
||||
this.credentials.credentials = {};
|
||||
}
|
||||
|
||||
if (provider) {
|
||||
if (provider.federated) {
|
||||
this.setTabId(FEDERATED_AUTH);
|
||||
} else {
|
||||
this.setTabId(getAuthProviderTabId(provider, configuration));
|
||||
}
|
||||
} else {
|
||||
this.setTabId(null);
|
||||
}
|
||||
},
|
||||
}),
|
||||
{
|
||||
@@ -84,7 +95,6 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string |
|
||||
activeConfiguration: observable.ref,
|
||||
credentials: observable,
|
||||
setActiveProvider: action,
|
||||
setActiveConfiguration: action,
|
||||
},
|
||||
false,
|
||||
);
|
||||
@@ -94,7 +104,11 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string |
|
||||
return true;
|
||||
}
|
||||
|
||||
if (provider.configurable || provider.trusted || provider.private) {
|
||||
if (provider.federated || provider.trusted || provider.private) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (provider.configurable && (provider.configurations?.length ?? 0) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -111,13 +125,25 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string |
|
||||
return false;
|
||||
});
|
||||
|
||||
const configurations = providers.filter(
|
||||
provider => provider.configurable && (provider.configurations?.length || 0) > 0 && authProvidersResource.resource.isAuthEnabled(provider.id),
|
||||
const federatedProviders = providers.filter(
|
||||
provider =>
|
||||
provider.federated &&
|
||||
provider.configurable &&
|
||||
(provider.configurations?.length || 0) > 0 &&
|
||||
authProvidersResource.resource.isAuthEnabled(provider.id),
|
||||
);
|
||||
|
||||
const tabIds = activeProviders.map(provider => provider.id);
|
||||
const tabIds = activeProviders
|
||||
.map(provider => {
|
||||
if (provider.configurable) {
|
||||
return provider.configurations?.map(configuration => getAuthProviderTabId(provider, configuration)) ?? [];
|
||||
}
|
||||
|
||||
if (configurations.length > 0) {
|
||||
return provider.id;
|
||||
})
|
||||
.flat();
|
||||
|
||||
if (federatedProviders.length > 0) {
|
||||
tabIds.push(FEDERATED_AUTH);
|
||||
}
|
||||
|
||||
@@ -147,9 +173,7 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string |
|
||||
|
||||
this.authenticating = true;
|
||||
try {
|
||||
if (configuration) {
|
||||
this.state.setActiveConfiguration(provider, configuration ?? null);
|
||||
}
|
||||
this.state.setActiveProvider(provider, configuration ?? null);
|
||||
|
||||
const loginTask = authInfoService.login(provider.id, {
|
||||
configurationId: configuration?.id,
|
||||
@@ -170,8 +194,9 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string |
|
||||
this.authTask = null;
|
||||
this.authenticating = false;
|
||||
|
||||
if (configuration) {
|
||||
this.state.setActiveConfiguration(null, null);
|
||||
if (provider.federated) {
|
||||
this.state.setActiveProvider(null, null);
|
||||
this.state.setTabId(FEDERATED_AUTH);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -188,7 +213,7 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string |
|
||||
state,
|
||||
adminPageActive,
|
||||
providers: activeProviders,
|
||||
configurations,
|
||||
federatedProviders,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -203,13 +228,11 @@ export function useAuthDialogState(accessRequest: boolean, providerId: string |
|
||||
);
|
||||
|
||||
if (tabIds.length > 0 && (state.tabId === null || !tabIds.includes(state.tabId))) {
|
||||
const tabId = tabIds[0];
|
||||
state.setTabId(tabId);
|
||||
const provider = providers.find(provider => provider.id === providerId) || activeProviders[0] || null;
|
||||
const configuration =
|
||||
provider?.configurations?.find(configuration => configuration.id === configurationId) || provider?.configurations?.[0] || null;
|
||||
|
||||
const provider = activeProviders.find(provider => provider.id === tabId) || providers.find(provider => provider.id === providerId) || null;
|
||||
const configuration = provider?.configurations?.find(configuration => configuration.id === configurationId) ?? null;
|
||||
|
||||
state.setActiveConfiguration(provider, configuration);
|
||||
state.setActiveProvider(provider, configuration);
|
||||
}
|
||||
|
||||
return data;
|
||||
@@ -229,3 +252,10 @@ function compareProviders(providerA: AuthProvider, providerB: AuthProvider): num
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
export function getAuthProviderTabId(provider: AuthProvider, configuration?: AuthProviderConfiguration | null): string {
|
||||
if (!configuration) {
|
||||
return provider.id;
|
||||
}
|
||||
return provider.id + '_' + configuration.id;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ export function matchType(type?: string) {
|
||||
case 'int':
|
||||
case 'double':
|
||||
case 'long':
|
||||
case 'Integer':
|
||||
return 'number';
|
||||
default:
|
||||
return 'text';
|
||||
|
||||
Reference in New Issue
Block a user