From 33c084dd0ff420e3ed0465577e983fcda483cdb1 Mon Sep 17 00:00:00 2001 From: Simon Hornby Date: Thu, 13 Oct 2022 10:13:41 +0200 Subject: [PATCH] feat: Add group sync settings to front end (#2183) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add group sync settings to front end Co-authored-by: Nuno Góis --- .../admin/auth/OidcAuth/OidcAuth.tsx | 16 +++- .../admin/auth/SamlAuth/SamlAuth.tsx | 16 ++++ .../component/admin/auth/SsoGroupSettings.tsx | 81 +++++++++++++++++++ frontend/src/interfaces/uiConfig.ts | 1 + .../__snapshots__/create-config.test.ts.snap | 2 + src/lib/types/experimental.ts | 5 ++ src/server-dev.ts | 1 + src/test/config/test-config.ts | 1 + 8 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 frontend/src/component/admin/auth/SsoGroupSettings.tsx diff --git a/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx b/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx index 721f65beff..383d750abc 100644 --- a/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx +++ b/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx @@ -17,12 +17,16 @@ import useAuthSettings from 'hooks/api/getters/useAuthSettings/useAuthSettings'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; import { removeEmptyStringFields } from 'utils/removeEmptyStringFields'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { SsoGroupSettings } from '../SsoGroupSettings'; const initialState = { enabled: false, enableSingleSignOut: false, + enableGroupSyncing: false, autoCreate: false, unleashHostname: location.hostname, + groupJsonPath: '', clientId: '', discoverUrl: '', secret: '', @@ -36,6 +40,7 @@ export const OidcAuth = () => { const { hasAccess } = useContext(AccessContext); const { config } = useAuthSettings('oidc'); const { updateSettings, errors, loading } = useAuthSettingsApi('oidc'); + const ssoSyncShown = Boolean(uiConfig.flags.syncSSOGroups); useEffect(() => { if (config.discoverUrl) { @@ -235,7 +240,16 @@ export const OidcAuth = () => { /> - + + } + /> diff --git a/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx b/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx index 5e1da6f385..7ecbded649 100644 --- a/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx +++ b/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx @@ -17,16 +17,20 @@ import useAuthSettings from 'hooks/api/getters/useAuthSettings/useAuthSettings'; import useAuthSettingsApi from 'hooks/api/actions/useAuthSettingsApi/useAuthSettingsApi'; import { formatUnknownError } from 'utils/formatUnknownError'; import { removeEmptyStringFields } from 'utils/removeEmptyStringFields'; +import { SsoGroupSettings } from '../SsoGroupSettings'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; const initialState = { enabled: false, autoCreate: false, + enableGroupSyncing: false, unleashHostname: location.hostname, entityId: '', signOnUrl: '', certificate: '', signOutUrl: '', spCertificate: '', + groupJsonPath: '', }; export const SamlAuth = () => { @@ -36,6 +40,7 @@ export const SamlAuth = () => { const { hasAccess } = useContext(AccessContext); const { config } = useAuthSettings('saml'); const { updateSettings, errors, loading } = useAuthSettingsApi('saml'); + const ssoSyncShown = Boolean(uiConfig.flags.syncSSOGroups); useEffect(() => { if (config.entityId) { @@ -246,6 +251,17 @@ export const SamlAuth = () => { /> + + } + /> + diff --git a/frontend/src/component/admin/auth/SsoGroupSettings.tsx b/frontend/src/component/admin/auth/SsoGroupSettings.tsx new file mode 100644 index 0000000000..e68ae4d0fb --- /dev/null +++ b/frontend/src/component/admin/auth/SsoGroupSettings.tsx @@ -0,0 +1,81 @@ +import React, { Fragment } from 'react'; +import { FormControlLabel, Grid, Switch, TextField } from '@mui/material'; + +interface SsoGroupSettingsProps { + ssoType: 'OIDC' | 'SAML'; + data?: { + enabled: boolean; + enableGroupSyncing: boolean; + groupJsonPath: string; + }; + setValue: (name: string, value: string | boolean) => void; +} + +export const SsoGroupSettings = ({ + ssoType, + data = { + enabled: false, + enableGroupSyncing: false, + groupJsonPath: '', + }, + setValue, +}: SsoGroupSettingsProps) => { + const updateGroupSyncing = () => { + setValue('enableGroupSyncing', !data.enableGroupSyncing); + }; + + const updateField = (event: React.ChangeEvent) => { + setValue(event.target.name, event.target.value); + }; + + return ( + <> + + + Enable Group Syncing +

+ Enables automatically syncing of users from the{' '} + {ssoType} + provider when a user logs in. +

+
+ + + } + label={data.enableGroupSyncing ? 'Enabled' : 'Disabled'} + /> + +
+ + + Group Field JSON Path +

+ Specifies the path in the {ssoType} token response from + which to read the groups the user belongs to. +

+
+ + + +
+ + ); +}; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index b70971135f..7bfc185b88 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -42,6 +42,7 @@ export interface IFlags { embedProxyFrontend?: boolean; publicSignup?: boolean; personalAccessTokens?: boolean; + syncSSOGroups?: boolean; } export interface IVersionInfo { diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index dedf17411f..df078cf2c6 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -74,6 +74,7 @@ exports[`should create default config 1`] = ` "personalAccessTokens": false, "publicSignup": false, "responseTimeWithAppName": false, + "syncSSOGroups": false, }, }, "flagResolver": FlagResolver { @@ -86,6 +87,7 @@ exports[`should create default config 1`] = ` "personalAccessTokens": false, "publicSignup": false, "responseTimeWithAppName": false, + "syncSSOGroups": false, }, "externalResolver": { "isEnabled": [Function], diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 90f2629bd2..95a69bba02 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -14,6 +14,10 @@ export const defaultExperimentalOptions = { process.env.UNLEASH_EXPERIMENTAL_PERSONAL_ACCESS_TOKENS, false, ), + syncSSOGroups: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_SYNC_SSO_GROUPS, + false, + ), embedProxyFrontend: parseEnvVarBoolean( process.env.UNLEASH_EXPERIMENTAL_EMBED_PROXY_FRONTEND, false, @@ -43,6 +47,7 @@ export interface IExperimentalOptions { batchMetrics?: boolean; anonymiseEventLog?: boolean; personalAccessTokens?: boolean; + syncSSOGroups?: boolean; }; externalResolver: IExternalFlagResolver; } diff --git a/src/server-dev.ts b/src/server-dev.ts index 5a4fb02d18..d7b85109de 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -39,6 +39,7 @@ process.nextTick(async () => { anonymiseEventLog: false, responseTimeWithAppName: true, personalAccessTokens: true, + syncSSOGroups: true, }, }, authentication: { diff --git a/src/test/config/test-config.ts b/src/test/config/test-config.ts index d11ee942a0..33a7ca50e6 100644 --- a/src/test/config/test-config.ts +++ b/src/test/config/test-config.ts @@ -28,6 +28,7 @@ export function createTestConfig(config?: IUnleashOptions): IUnleashConfig { embedProxyFrontend: true, batchMetrics: true, personalAccessTokens: true, + syncSSOGroups: true, }, }, };