From 793cd76e93294dbbe4b12e3bb69af2809213cafa Mon Sep 17 00:00:00 2001 From: David Leek Date: Thu, 16 May 2024 08:01:32 +0200 Subject: [PATCH] feat: move SCIM config into separate tab (#7055) Moves SCIM config back into separate tab ![image](https://github.com/Unleash/unleash/assets/707867/910f2128-08de-4460-a9e8-a606342669cd) --- .../src/component/admin/auth/AuthSettings.tsx | 11 +- .../admin/auth/SamlAuth/SamlAuth.tsx | 50 ---- .../admin/auth/ScimSettings/ScimSettings.tsx | 217 ++++++++++-------- frontend/src/hooks/useScim.ts | 66 ------ 4 files changed, 126 insertions(+), 218 deletions(-) delete mode 100644 frontend/src/hooks/useScim.ts diff --git a/frontend/src/component/admin/auth/AuthSettings.tsx b/frontend/src/component/admin/auth/AuthSettings.tsx index 255dd7e754..6cb5ad82d7 100644 --- a/frontend/src/component/admin/auth/AuthSettings.tsx +++ b/frontend/src/component/admin/auth/AuthSettings.tsx @@ -2,8 +2,10 @@ import { Alert, Tab, Tabs } from '@mui/material'; import { PageContent } from 'component/common/PageContent/PageContent'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { useUiFlag } from 'hooks/useUiFlag'; import { OidcAuth } from './OidcAuth/OidcAuth'; import { SamlAuth } from './SamlAuth/SamlAuth'; +import { ScimSettings } from './ScimSettings/ScimSettings'; import { PasswordAuth } from './PasswordAuth/PasswordAuth'; import { GoogleAuth } from './GoogleAuth/GoogleAuth'; import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard'; @@ -14,7 +16,7 @@ import { TabPanel } from 'component/common/TabNav/TabPanel/TabPanel'; export const AuthSettings = () => { const { authenticationType } = useUiConfig().uiConfig; - const { uiConfig } = useUiConfig(); + const { uiConfig, isEnterprise } = useUiConfig(); const tabs = [ { @@ -37,6 +39,13 @@ export const AuthSettings = () => { (item) => uiConfig.flags?.googleAuthEnabled || item.label !== 'Google', ); + if (isEnterprise() && useUiFlag('scimApi')) { + tabs.push({ + label: 'SCIM', + component: , + }); + } + const [activeTab, setActiveTab] = useState(0); return ( diff --git a/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx b/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx index fa4ef5bc94..88d3b2ee17 100644 --- a/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx +++ b/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx @@ -17,10 +17,6 @@ import { formatUnknownError } from 'utils/formatUnknownError'; import { removeEmptyStringFields } from 'utils/removeEmptyStringFields'; import { SsoGroupSettings } from '../SsoGroupSettings'; 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 = { 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) => { event.preventDefault(); @@ -107,9 +85,6 @@ export const SamlAuth = () => { title: 'Settings stored', type: 'success', }); - if (scimEnabled) { - saveScimSettings(); - } } catch (error: unknown) { setToastApiError(formatUnknownError(error)); } @@ -288,31 +263,6 @@ export const SamlAuth = () => { setValue={setValue} /> - - } - /> - >; - assumeControlOfExisting: boolean; - setAssumeControlOfExisting: React.Dispatch>; - newToken: string; - settings: ScimSettings; - tokenGenerationDialog: boolean; - setTokenGenerationDialog: React.Dispatch>; - onGenerateNewTokenConfirm: () => void; - tokenDialog: boolean; - setTokenDialog: React.Dispatch>; -} +const StyledContainer = styled('div')(({ theme }) => ({ + padding: theme.spacing(3), + border: `1px solid ${theme.palette.divider}`, + borderRadius: theme.shape.borderRadiusLarge, +})); -export const ScimConfigSettings = ({ - disabled, - loading, - enabled, - setEnabled, - assumeControlOfExisting, - setAssumeControlOfExisting, - newToken, - settings, - tokenGenerationDialog, - setTokenGenerationDialog, - onGenerateNewTokenConfirm, - tokenDialog, - setTokenDialog, -}: IScimSettingsParameters) => { +const StyledTitleDiv = styled('div')(({ theme }) => ({ + marginBottom: theme.spacing(1), +})); + +export const ScimSettings = () => { 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 () => { 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 ( <> -

SCIM Provisioning

@@ -63,77 +86,69 @@ export const ScimConfigSettings = ({ - - - Enable -

Enable SCIM provisioning.

+ + + + + SCIM provisioning + +

+ 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. +

+
+ + { + saveScimSettings(enabled); + }} + value={enabled} + name='enabled' + checked={enabled} + /> + } + label={enabled ? 'Enabled' : 'Disabled'} + /> +
- - setEnabled(enabled)} - value={enabled} - name='enabled' - checked={enabled} - disabled={disabled} - /> - } - label={enabled ? 'Enabled' : 'Disabled'} - /> - -
- - - Assume control -

Assumes control of users and groups

+ + + + Generate new token + + } + /> + - - - setAssumeControlOfExisting(set_enabled) - } - value={assumeControlOfExisting} - name='assumeControlOfExisting' - checked={assumeControlOfExisting} - disabled={disabled} - /> - } - label={assumeControlOfExisting ? 'Enabled' : 'Disabled'} - /> - -
- - - - - Generate new token - - } - /> - - - - + + + ); }; diff --git a/frontend/src/hooks/useScim.ts b/frontend/src/hooks/useScim.ts deleted file mode 100644 index 80a1ebaa8f..0000000000 --- a/frontend/src/hooks/useScim.ts +++ /dev/null @@ -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, - }; -};