mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-17 01:17:29 +02:00
feat: move SCIM config into separate tab (#7055)
Moves SCIM config back into separate tab 
This commit is contained in:
parent
acb663df7a
commit
793cd76e93
@ -2,8 +2,10 @@ import { Alert, Tab, Tabs } from '@mui/material';
|
|||||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
import { OidcAuth } from './OidcAuth/OidcAuth';
|
import { OidcAuth } from './OidcAuth/OidcAuth';
|
||||||
import { SamlAuth } from './SamlAuth/SamlAuth';
|
import { SamlAuth } from './SamlAuth/SamlAuth';
|
||||||
|
import { ScimSettings } from './ScimSettings/ScimSettings';
|
||||||
import { PasswordAuth } from './PasswordAuth/PasswordAuth';
|
import { PasswordAuth } from './PasswordAuth/PasswordAuth';
|
||||||
import { GoogleAuth } from './GoogleAuth/GoogleAuth';
|
import { GoogleAuth } from './GoogleAuth/GoogleAuth';
|
||||||
import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
|
import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
|
||||||
@ -14,7 +16,7 @@ import { TabPanel } from 'component/common/TabNav/TabPanel/TabPanel';
|
|||||||
|
|
||||||
export const AuthSettings = () => {
|
export const AuthSettings = () => {
|
||||||
const { authenticationType } = useUiConfig().uiConfig;
|
const { authenticationType } = useUiConfig().uiConfig;
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig, isEnterprise } = useUiConfig();
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
@ -37,6 +39,13 @@ export const AuthSettings = () => {
|
|||||||
(item) => uiConfig.flags?.googleAuthEnabled || item.label !== 'Google',
|
(item) => uiConfig.flags?.googleAuthEnabled || item.label !== 'Google',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (isEnterprise() && useUiFlag('scimApi')) {
|
||||||
|
tabs.push({
|
||||||
|
label: 'SCIM',
|
||||||
|
component: <ScimSettings />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -17,10 +17,6 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
|||||||
import { removeEmptyStringFields } from 'utils/removeEmptyStringFields';
|
import { removeEmptyStringFields } from 'utils/removeEmptyStringFields';
|
||||||
import { SsoGroupSettings } from '../SsoGroupSettings';
|
import { SsoGroupSettings } from '../SsoGroupSettings';
|
||||||
import type { IRole } from 'interfaces/role';
|
import type { IRole } from 'interfaces/role';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
import { useScim } from 'hooks/useScim';
|
|
||||||
import { ScimConfigSettings } from '../ScimSettings/ScimSettings';
|
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@ -80,24 +76,6 @@ export const SamlAuth = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const scimEnabled = useUiFlag('scimApi');
|
|
||||||
|
|
||||||
const {
|
|
||||||
settings,
|
|
||||||
enabled,
|
|
||||||
setEnabled,
|
|
||||||
assumeControlOfExisting,
|
|
||||||
setAssumeControlOfExisting,
|
|
||||||
newToken,
|
|
||||||
tokenGenerationDialog,
|
|
||||||
setTokenGenerationDialog,
|
|
||||||
tokenDialog,
|
|
||||||
setTokenDialog,
|
|
||||||
loading: scimLoading,
|
|
||||||
saveScimSettings,
|
|
||||||
onGenerateNewTokenConfirm,
|
|
||||||
} = useScim();
|
|
||||||
|
|
||||||
const onSubmit = async (event: React.SyntheticEvent) => {
|
const onSubmit = async (event: React.SyntheticEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
@ -107,9 +85,6 @@ export const SamlAuth = () => {
|
|||||||
title: 'Settings stored',
|
title: 'Settings stored',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
if (scimEnabled) {
|
|
||||||
saveScimSettings();
|
|
||||||
}
|
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
setToastApiError(formatUnknownError(error));
|
setToastApiError(formatUnknownError(error));
|
||||||
}
|
}
|
||||||
@ -288,31 +263,6 @@ export const SamlAuth = () => {
|
|||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={scimEnabled}
|
|
||||||
show={
|
|
||||||
<ScimConfigSettings
|
|
||||||
disabled={!data.enabled}
|
|
||||||
settings={settings}
|
|
||||||
enabled={enabled}
|
|
||||||
setEnabled={setEnabled}
|
|
||||||
assumeControlOfExisting={assumeControlOfExisting}
|
|
||||||
setAssumeControlOfExisting={
|
|
||||||
setAssumeControlOfExisting
|
|
||||||
}
|
|
||||||
newToken={newToken}
|
|
||||||
tokenGenerationDialog={tokenGenerationDialog}
|
|
||||||
setTokenGenerationDialog={setTokenGenerationDialog}
|
|
||||||
tokenDialog={tokenDialog}
|
|
||||||
setTokenDialog={setTokenDialog}
|
|
||||||
loading={scimLoading}
|
|
||||||
onGenerateNewTokenConfirm={
|
|
||||||
onGenerateNewTokenConfirm
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<AutoCreateForm
|
<AutoCreateForm
|
||||||
data={data}
|
data={data}
|
||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
|
@ -1,51 +1,74 @@
|
|||||||
import { Button, FormControlLabel, Grid, Switch } from '@mui/material';
|
import { Button, FormControlLabel, Grid, Switch, styled } from '@mui/material';
|
||||||
import { Alert } from '@mui/material';
|
import { Alert } from '@mui/material';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { ScimTokenGenerationDialog } from './ScimTokenGenerationDialog';
|
import { ScimTokenGenerationDialog } from './ScimTokenGenerationDialog';
|
||||||
import { ScimTokenDialog } from './ScimTokenDialog';
|
import { ScimTokenDialog } from './ScimTokenDialog';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import type { ScimSettings } from 'hooks/api/getters/useScimSettings/useScimSettings';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
|
import useToast from 'hooks/useToast';
|
||||||
|
import { useScimSettingsApi } from 'hooks/api/actions/useScimSettingsApi/useScimSettingsApi';
|
||||||
|
import { useScimSettings } from 'hooks/api/getters/useScimSettings/useScimSettings';
|
||||||
|
|
||||||
export interface IScimSettingsParameters {
|
const StyledContainer = styled('div')(({ theme }) => ({
|
||||||
disabled: boolean;
|
padding: theme.spacing(3),
|
||||||
loading: boolean;
|
border: `1px solid ${theme.palette.divider}`,
|
||||||
enabled: boolean;
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
setEnabled: React.Dispatch<React.SetStateAction<boolean>>;
|
}));
|
||||||
assumeControlOfExisting: boolean;
|
|
||||||
setAssumeControlOfExisting: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
newToken: string;
|
|
||||||
settings: ScimSettings;
|
|
||||||
tokenGenerationDialog: boolean;
|
|
||||||
setTokenGenerationDialog: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
onGenerateNewTokenConfirm: () => void;
|
|
||||||
tokenDialog: boolean;
|
|
||||||
setTokenDialog: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ScimConfigSettings = ({
|
const StyledTitleDiv = styled('div')(({ theme }) => ({
|
||||||
disabled,
|
marginBottom: theme.spacing(1),
|
||||||
loading,
|
}));
|
||||||
enabled,
|
|
||||||
setEnabled,
|
export const ScimSettings = () => {
|
||||||
assumeControlOfExisting,
|
|
||||||
setAssumeControlOfExisting,
|
|
||||||
newToken,
|
|
||||||
settings,
|
|
||||||
tokenGenerationDialog,
|
|
||||||
setTokenGenerationDialog,
|
|
||||||
onGenerateNewTokenConfirm,
|
|
||||||
tokenDialog,
|
|
||||||
setTokenDialog,
|
|
||||||
}: IScimSettingsParameters) => {
|
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
|
const { setToastData, setToastApiError } = useToast();
|
||||||
|
const [newToken, setNewToken] = useState('');
|
||||||
|
const [tokenGenerationDialog, setTokenGenerationDialog] = useState(false);
|
||||||
|
const [tokenDialog, setTokenDialog] = useState(false);
|
||||||
|
const { settings, refetch } = useScimSettings();
|
||||||
|
const [enabled, setEnabled] = useState(settings.enabled ?? true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setEnabled(settings.enabled ?? false);
|
||||||
|
}, [settings]);
|
||||||
|
|
||||||
|
const { saveSettings, generateNewToken, errors, loading } =
|
||||||
|
useScimSettingsApi();
|
||||||
|
|
||||||
const onGenerateNewToken = async () => {
|
const onGenerateNewToken = async () => {
|
||||||
setTokenGenerationDialog(true);
|
setTokenGenerationDialog(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onGenerateNewTokenConfirm = async () => {
|
||||||
|
setTokenGenerationDialog(false);
|
||||||
|
const token = await generateNewToken();
|
||||||
|
setNewToken(token);
|
||||||
|
setTokenDialog(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveScimSettings = async (enabled: boolean) => {
|
||||||
|
try {
|
||||||
|
setEnabled(enabled);
|
||||||
|
await saveSettings({ enabled, assumeControlOfExisting: false });
|
||||||
|
if (enabled && !settings.hasToken) {
|
||||||
|
const token = await generateNewToken();
|
||||||
|
setNewToken(token);
|
||||||
|
setTokenDialog(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
setToastData({
|
||||||
|
title: 'Settings stored',
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
await refetch();
|
||||||
|
} catch (error: unknown) {
|
||||||
|
setToastApiError(formatUnknownError(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h3>SCIM Provisioning</h3>
|
|
||||||
<Grid container sx={{ mb: 3 }}>
|
<Grid container sx={{ mb: 3 }}>
|
||||||
<Grid item md={12}>
|
<Grid item md={12}>
|
||||||
<Alert severity='info'>
|
<Alert severity='info'>
|
||||||
@ -63,20 +86,34 @@ export const ScimConfigSettings = ({
|
|||||||
</Alert>
|
</Alert>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<StyledContainer>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item md={5} mb={2}>
|
<Grid item md={10.5} mb={2}>
|
||||||
<strong>Enable</strong>
|
<StyledTitleDiv>
|
||||||
<p>Enable SCIM provisioning.</p>
|
<strong>SCIM provisioning</strong>
|
||||||
|
</StyledTitleDiv>
|
||||||
|
<p>
|
||||||
|
Enables SCIM provisioning. If SCIM provisioning has
|
||||||
|
not previously been enabled here this will also set
|
||||||
|
up a new auth token to use with your SCIM client,
|
||||||
|
and display it to the user. After the dialog has
|
||||||
|
been closed, this token will not be displayed again.
|
||||||
|
If you need a new token you can click the Generate
|
||||||
|
new token button below which will replace the old
|
||||||
|
token with a new token, and similarly display the
|
||||||
|
new token one time to the user.
|
||||||
|
</p>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item md={6}>
|
<Grid item md={1.5}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={
|
control={
|
||||||
<Switch
|
<Switch
|
||||||
onChange={(_, enabled) => setEnabled(enabled)}
|
onChange={(_, enabled) => {
|
||||||
|
saveScimSettings(enabled);
|
||||||
|
}}
|
||||||
value={enabled}
|
value={enabled}
|
||||||
name='enabled'
|
name='enabled'
|
||||||
checked={enabled}
|
checked={enabled}
|
||||||
disabled={disabled}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={enabled ? 'Enabled' : 'Disabled'}
|
label={enabled ? 'Enabled' : 'Disabled'}
|
||||||
@ -84,29 +121,6 @@ export const ScimConfigSettings = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
<Grid item md={5} mb={2}>
|
|
||||||
<strong>Assume control</strong>
|
|
||||||
<p>Assumes control of users and groups</p>
|
|
||||||
</Grid>
|
|
||||||
<Grid item md={6}>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Switch
|
|
||||||
onChange={(_, set_enabled) =>
|
|
||||||
setAssumeControlOfExisting(set_enabled)
|
|
||||||
}
|
|
||||||
value={assumeControlOfExisting}
|
|
||||||
name='assumeControlOfExisting'
|
|
||||||
checked={assumeControlOfExisting}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={assumeControlOfExisting ? 'Enabled' : 'Disabled'}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item md={5} mb={2}>
|
<Grid item md={5} mb={2}>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
@ -134,6 +148,7 @@ export const ScimConfigSettings = ({
|
|||||||
setOpen={setTokenDialog}
|
setOpen={setTokenDialog}
|
||||||
token={newToken}
|
token={newToken}
|
||||||
/>
|
/>
|
||||||
|
</StyledContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
import { useScimSettingsApi } from 'hooks/api/actions/useScimSettingsApi/useScimSettingsApi';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
|
||||||
import useToast from 'hooks/useToast';
|
|
||||||
import { useScimSettings } from './api/getters/useScimSettings/useScimSettings';
|
|
||||||
|
|
||||||
export const useScim = () => {
|
|
||||||
const [newToken, setNewToken] = useState('');
|
|
||||||
const [enabled, setEnabled] = useState(false);
|
|
||||||
const [tokenGenerationDialog, setTokenGenerationDialog] = useState(false);
|
|
||||||
const [tokenDialog, setTokenDialog] = useState(false);
|
|
||||||
const [assumeControlOfExisting, setAssumeControlOfExisting] =
|
|
||||||
useState(false);
|
|
||||||
|
|
||||||
const { saveSettings, generateNewToken, errors, loading } =
|
|
||||||
useScimSettingsApi();
|
|
||||||
|
|
||||||
const { settings, refetch } = useScimSettings();
|
|
||||||
const { setToastData, setToastApiError } = useToast();
|
|
||||||
const saveScimSettings = async () => {
|
|
||||||
try {
|
|
||||||
await saveSettings({ enabled, assumeControlOfExisting });
|
|
||||||
if (enabled && !settings.hasToken) {
|
|
||||||
const token = await generateNewToken();
|
|
||||||
setNewToken(token);
|
|
||||||
setTokenDialog(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
setToastData({
|
|
||||||
title: 'Settings stored',
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
refetch();
|
|
||||||
} catch (error: unknown) {
|
|
||||||
setToastApiError(formatUnknownError(error));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onGenerateNewTokenConfirm = async () => {
|
|
||||||
setTokenGenerationDialog(false);
|
|
||||||
const token = await generateNewToken();
|
|
||||||
setNewToken(token);
|
|
||||||
setTokenDialog(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setEnabled(settings.enabled ?? false);
|
|
||||||
setAssumeControlOfExisting(settings.assumeControlOfExisting ?? false);
|
|
||||||
}, [settings]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
settings,
|
|
||||||
enabled,
|
|
||||||
setEnabled,
|
|
||||||
assumeControlOfExisting,
|
|
||||||
setAssumeControlOfExisting,
|
|
||||||
newToken,
|
|
||||||
tokenGenerationDialog,
|
|
||||||
setTokenGenerationDialog,
|
|
||||||
tokenDialog,
|
|
||||||
setTokenDialog,
|
|
||||||
loading,
|
|
||||||
saveScimSettings,
|
|
||||||
onGenerateNewTokenConfirm,
|
|
||||||
};
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user