*/
import classNames from 'classnames';
import * as React from 'react';
+import { useCallback, useEffect, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { useSearchParams } from 'react-router-dom';
+import { getSystemInfo } from '../../../../api/system';
import withAvailableFeatures, {
WithAvailableFeaturesProps,
} from '../../../../app/components/available-features/withAvailableFeatures';
import { AlmKeys } from '../../../../types/alm-settings';
import { Feature } from '../../../../types/features';
import { ExtendedSettingDefinition } from '../../../../types/settings';
+import { SysInfoCluster } from '../../../../types/types';
import { AUTHENTICATION_CATEGORY } from '../../constants';
import CategoryDefinitionsList from '../CategoryDefinitionsList';
-import GithubAithentication from './GithubAutheticationTab';
+import GithubAuthenticationTab from './GithubAuthenticationTab';
import SamlAuthenticationTab, { SAML } from './SamlAuthenticationTab';
interface Props {
const { definitions } = props;
const [query, setSearchParams] = useSearchParams();
+ const [provider, setProvider] = useState<string>();
+
+ const loadProvider = useCallback(async () => {
+ const info = (await getSystemInfo()) as SysInfoCluster;
+ setProvider(info.System['External Users and Groups Provisioning']);
+ }, []);
+
+ useEffect(() => {
+ loadProvider();
+ }, []);
const currentTab = (query.get('tab') || SAML) as AuthenticationTabs;
{tab.key === SAML && (
<SamlAuthenticationTab
definitions={definitions.filter((def) => def.subCategory === SAML)}
+ provider={provider}
+ onReload={() => loadProvider()}
/>
)}
{tab.key === AlmKeys.GitHub && (
- <GithubAithentication
+ <GithubAuthenticationTab
definitions={definitions.filter((def) => def.subCategory === AlmKeys.GitHub)}
+ provider={provider}
+ onReload={() => loadProvider()}
/>
)}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { isEmpty } from 'lodash';
+import React, { useState } from 'react';
+import { FormattedMessage } from 'react-intl';
+import {
+ activateGithubProvisioning,
+ deactivateGithubProvisioning,
+ resetSettingValue,
+ setSettingValue,
+} from '../../../../api/settings';
+import DocLink from '../../../../components/common/DocLink';
+import ConfirmModal from '../../../../components/controls/ConfirmModal';
+import RadioCard from '../../../../components/controls/RadioCard';
+import { Button, ResetButtonLink, SubmitButton } from '../../../../components/controls/buttons';
+import CheckIcon from '../../../../components/icons/CheckIcon';
+import DeleteIcon from '../../../../components/icons/DeleteIcon';
+import EditIcon from '../../../../components/icons/EditIcon';
+import { Alert } from '../../../../components/ui/Alert';
+import { translate } from '../../../../helpers/l10n';
+import { AlmKeys } from '../../../../types/alm-settings';
+import { ExtendedSettingDefinition } from '../../../../types/settings';
+import { DOCUMENTATION_LINK_SUFFIXES } from './Authentication';
+import AuthenticationFormField from './AuthenticationFormField';
+import ConfigurationForm from './ConfigurationForm';
+import useGithubConfiguration, {
+ GITHUB_ENABLED_FIELD,
+ GITHUB_JIT_FIELDS,
+} from './hook/useGithubConfiguration';
+
+interface GithubAuthenticationProps {
+ definitions: ExtendedSettingDefinition[];
+ provider: string | undefined;
+ onReload: () => void;
+}
+
+const GITHUB_EXCLUDED_FIELD = [
+ 'sonar.auth.github.enabled',
+ 'sonar.auth.github.groupsSync',
+ 'sonar.auth.github.allowUsersToSignUp',
+ 'sonar.auth.github.organizations',
+];
+
+export default function GithubAuthenticationTab(props: GithubAuthenticationProps) {
+ const { definitions, provider } = props;
+ const [showEditModal, setShowEditModal] = useState(false);
+ const [showConfirmProvisioningModal, setShowConfirmProvisioningModal] = useState(false);
+
+ const {
+ hasConfiguration,
+ hasGithubProvisioning,
+ githubProvisioningStatus,
+ loading,
+ values,
+ setNewValue,
+ canBeSave,
+ reload,
+ url,
+ appId,
+ enabled,
+ deleteConfiguration,
+ newGithubProvisioningStatus,
+ setNewGithubProvisioningStatus,
+ hasGithubProvisioningConfigChange,
+ resetJitSetting,
+ } = useGithubConfiguration(definitions, props.onReload);
+
+ const hasDifferentProvider = provider !== undefined && provider !== 'github';
+
+ const handleCreateConfiguration = () => {
+ setShowEditModal(true);
+ };
+
+ const handleCancelConfiguration = () => {
+ setShowEditModal(false);
+ };
+
+ const handleConfirmChangeProvisioning = async () => {
+ if (newGithubProvisioningStatus && newGithubProvisioningStatus !== githubProvisioningStatus) {
+ await activateGithubProvisioning();
+ await reload();
+ } else {
+ if (newGithubProvisioningStatus !== githubProvisioningStatus) {
+ await deactivateGithubProvisioning();
+ }
+ await handleSaveGroup();
+ }
+ };
+
+ const handleSaveGroup = async () => {
+ await Promise.all(
+ GITHUB_JIT_FIELDS.map(async (settingKey) => {
+ const value = values[settingKey];
+ if (value.newValue !== undefined) {
+ if (isEmpty(value.newValue) && typeof value.newValue !== 'boolean') {
+ await resetSettingValue({ keys: value.definition.key });
+ } else {
+ await setSettingValue(value.definition, value.newValue);
+ }
+ }
+ })
+ );
+ await reload();
+ };
+
+ const handleToggleEnable = async () => {
+ const value = values[GITHUB_ENABLED_FIELD];
+ await setSettingValue(value.definition, !enabled);
+ await reload();
+ };
+
+ return (
+ <div className="authentication-configuration">
+ <div className="spacer-bottom display-flex-space-between display-flex-center">
+ <h4>{translate('settings.authentication.github.configuration')}</h4>
+
+ {!hasConfiguration && (
+ <div>
+ <Button onClick={handleCreateConfiguration}>
+ {translate('settings.authentication.form.create')}
+ </Button>
+ </div>
+ )}
+ </div>
+ {!hasConfiguration ? (
+ <div className="big-padded text-center huge-spacer-bottom authentication-no-config">
+ {translate('settings.authentication.github.form.not_configured')}
+ </div>
+ ) : (
+ <>
+ <div className="spacer-bottom big-padded bordered display-flex-space-between">
+ <div>
+ <h5>{appId}</h5>
+ <p>{url}</p>
+ <p className="big-spacer-top big-spacer-bottom">
+ {enabled ? (
+ <span className="authentication-enabled spacer-left">
+ <CheckIcon className="spacer-right" />
+ {translate('settings.authentication.form.enabled')}
+ </span>
+ ) : (
+ translate('settings.authentication.form.not_enabled')
+ )}
+ </p>
+ <Button className="spacer-top" onClick={handleToggleEnable}>
+ {enabled
+ ? translate('settings.authentication.form.disable')
+ : translate('settings.authentication.form.enable')}
+ </Button>
+ </div>
+ <div>
+ <Button className="spacer-right" onClick={handleCreateConfiguration}>
+ <EditIcon />
+ {translate('settings.authentication.form.edit')}
+ </Button>
+ <Button className="button-red" disabled={enabled} onClick={deleteConfiguration}>
+ <DeleteIcon />
+ {translate('settings.authentication.form.delete')}
+ </Button>
+ </div>
+ </div>
+ <div className="spacer-bottom big-padded bordered display-flex-space-between">
+ <form
+ onSubmit={async (e) => {
+ e.preventDefault();
+ if (newGithubProvisioningStatus !== githubProvisioningStatus) {
+ setShowConfirmProvisioningModal(true);
+ } else {
+ await handleSaveGroup();
+ }
+ }}
+ >
+ <fieldset className="display-flex-column big-spacer-bottom">
+ <label className="h5">
+ {translate('settings.authentication.form.provisioning')}
+ </label>
+
+ {enabled ? (
+ <div className="display-flex-row spacer-top">
+ <RadioCard
+ label={translate(
+ 'settings.authentication.github.form.provisioning_with_github'
+ )}
+ title={translate(
+ 'settings.authentication.github.form.provisioning_with_github'
+ )}
+ selected={newGithubProvisioningStatus ?? githubProvisioningStatus}
+ onClick={() => setNewGithubProvisioningStatus(true)}
+ disabled={!hasGithubProvisioning || hasDifferentProvider}
+ >
+ {hasGithubProvisioning ? (
+ <>
+ {hasDifferentProvider && (
+ <p className="spacer-bottom text-bold">
+ {translate('settings.authentication.form.other_provisioning_enabled')}
+ </p>
+ )}
+ <p className="spacer-bottom">
+ {translate(
+ 'settings.authentication.github.form.provisioning_with_github.description'
+ )}
+ </p>
+ <p>
+ <FormattedMessage
+ id="settings.authentication.github.form.provisioning_with_github.description.doc"
+ defaultMessage={translate(
+ 'settings.authentication.github.form.provisioning_with_github.description.doc'
+ )}
+ values={{
+ documentation: (
+ <DocLink
+ to={`/instance-administration/authentication/${
+ DOCUMENTATION_LINK_SUFFIXES[AlmKeys.GitHub]
+ }/`}
+ >
+ {translate('documentation')}
+ </DocLink>
+ ),
+ }}
+ />
+ </p>
+ </>
+ ) : (
+ <p>
+ <FormattedMessage
+ id="settings.authentication.github.form.provisioning.disabled"
+ defaultMessage={translate(
+ 'settings.authentication.github.form.provisioning.disabled'
+ )}
+ values={{
+ documentation: (
+ // Documentation page not ready yet.
+ <DocLink to="/instance-administration/authentication/github">
+ {translate('documentation')}
+ </DocLink>
+ ),
+ }}
+ />
+ </p>
+ )}
+ </RadioCard>
+ <RadioCard
+ label={translate('settings.authentication.form.provisioning_at_login')}
+ title={translate('settings.authentication.form.provisioning_at_login')}
+ selected={!(newGithubProvisioningStatus ?? githubProvisioningStatus)}
+ onClick={() => setNewGithubProvisioningStatus(false)}
+ >
+ {Object.values(values).map((val) => {
+ if (!GITHUB_JIT_FIELDS.includes(val.key)) {
+ return null;
+ }
+ return (
+ <div key={val.key}>
+ <AuthenticationFormField
+ settingValue={values[val.key]?.newValue ?? values[val.key]?.value}
+ definition={val.definition}
+ mandatory={val.mandatory}
+ onFieldChange={setNewValue}
+ isNotSet={val.isNotSet}
+ />
+ </div>
+ );
+ })}
+ </RadioCard>
+ </div>
+ ) : (
+ <Alert className="big-spacer-top" variant="info">
+ {translate('settings.authentication.github.enable_first')}
+ </Alert>
+ )}
+ </fieldset>
+ {enabled && (
+ <>
+ <SubmitButton disabled={!hasGithubProvisioningConfigChange}>
+ {translate('save')}
+ </SubmitButton>
+ <ResetButtonLink
+ className="spacer-left"
+ onClick={() => {
+ setNewGithubProvisioningStatus(undefined);
+ resetJitSetting();
+ }}
+ disabled={!hasGithubProvisioningConfigChange}
+ >
+ {translate('cancel')}
+ </ResetButtonLink>
+ </>
+ )}
+ {showConfirmProvisioningModal && (
+ <ConfirmModal
+ onConfirm={() => handleConfirmChangeProvisioning()}
+ header={translate(
+ 'settings.authentication.github.confirm',
+ newGithubProvisioningStatus ? 'auto' : 'jit'
+ )}
+ onClose={() => setShowConfirmProvisioningModal(false)}
+ isDestructive={!newGithubProvisioningStatus}
+ confirmButtonText={translate('yes')}
+ >
+ {translate(
+ 'settings.authentication.github.confirm',
+ newGithubProvisioningStatus ? 'auto' : 'jit',
+ 'description'
+ )}
+ </ConfirmModal>
+ )}
+ </form>
+ </div>
+ </>
+ )}
+
+ {showEditModal && (
+ <ConfigurationForm
+ tab={AlmKeys.GitHub}
+ excludedField={GITHUB_EXCLUDED_FIELD}
+ loading={loading}
+ values={values}
+ setNewValue={setNewValue}
+ canBeSave={canBeSave}
+ onClose={handleCancelConfiguration}
+ create={!hasConfiguration}
+ onReload={reload}
+ />
+ )}
+ </div>
+ );
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { isEmpty } from 'lodash';
-import React, { useState } from 'react';
-import { FormattedMessage } from 'react-intl';
-import {
- activateGithubProvisioning,
- deactivateGithubProvisioning,
- resetSettingValue,
- setSettingValue,
-} from '../../../../api/settings';
-import DocLink from '../../../../components/common/DocLink';
-import ConfirmModal from '../../../../components/controls/ConfirmModal';
-import RadioCard from '../../../../components/controls/RadioCard';
-import { Button, ResetButtonLink, SubmitButton } from '../../../../components/controls/buttons';
-import CheckIcon from '../../../../components/icons/CheckIcon';
-import DeleteIcon from '../../../../components/icons/DeleteIcon';
-import EditIcon from '../../../../components/icons/EditIcon';
-import { Alert } from '../../../../components/ui/Alert';
-import { translate } from '../../../../helpers/l10n';
-import { AlmKeys } from '../../../../types/alm-settings';
-import { ExtendedSettingDefinition } from '../../../../types/settings';
-import { DOCUMENTATION_LINK_SUFFIXES } from './Authentication';
-import AuthenticationFormField from './AuthenticationFormField';
-import ConfigurationForm from './ConfigurationForm';
-import useGithubConfiguration, {
- GITHUB_ENABLED_FIELD,
- GITHUB_JIT_FIELDS,
-} from './hook/useGithubConfiguration';
-
-interface GithubAuthenticationProps {
- definitions: ExtendedSettingDefinition[];
-}
-
-const GITHUB_EXCLUDED_FIELD = [
- 'sonar.auth.github.enabled',
- 'sonar.auth.github.groupsSync',
- 'sonar.auth.github.allowUsersToSignUp',
- 'sonar.auth.github.organizations',
-];
-
-export default function GithubAithentication(props: GithubAuthenticationProps) {
- const [showEditModal, setShowEditModal] = useState(false);
- const [showConfirmProvisioningModal, setShowConfirmProvisioningModal] = useState(false);
-
- const {
- hasConfiguration,
- hasGithubProvisioning,
- githubProvisioningStatus,
- loading,
- values,
- setNewValue,
- canBeSave,
- reload,
- url,
- appId,
- enabled,
- deleteConfiguration,
- newGithubProvisioningStatus,
- setNewGithubProvisioningStatus,
- hasGithubProvisioningConfigChange,
- resetJitSetting,
- } = useGithubConfiguration(props.definitions);
-
- const handleCreateConfiguration = () => {
- setShowEditModal(true);
- };
-
- const handleCancelConfiguration = () => {
- setShowEditModal(false);
- };
-
- const handleConfirmChangeProvisioning = async () => {
- if (newGithubProvisioningStatus && newGithubProvisioningStatus !== githubProvisioningStatus) {
- await activateGithubProvisioning();
- await reload();
- } else {
- if (newGithubProvisioningStatus !== githubProvisioningStatus) {
- await deactivateGithubProvisioning();
- }
- await handleSaveGroup();
- }
- };
-
- const handleSaveGroup = async () => {
- await Promise.all(
- GITHUB_JIT_FIELDS.map(async (settingKey) => {
- const value = values[settingKey];
- if (value.newValue !== undefined) {
- // isEmpty always return true for booleans...
- if (isEmpty(value.newValue) && typeof value.newValue !== 'boolean') {
- await resetSettingValue({ keys: value.definition.key });
- } else {
- await setSettingValue(value.definition, value.newValue);
- }
- }
- })
- );
- await reload();
- };
-
- const handleToggleEnable = async () => {
- const value = values[GITHUB_ENABLED_FIELD];
- await setSettingValue(value.definition, !enabled);
- await reload();
- };
-
- return (
- <div className="authentication-configuration">
- <div className="spacer-bottom display-flex-space-between display-flex-center">
- <h4>{translate('settings.authentication.github.configuration')}</h4>
-
- {!hasConfiguration && (
- <div>
- <Button onClick={handleCreateConfiguration}>
- {translate('settings.authentication.form.create')}
- </Button>
- </div>
- )}
- </div>
- {!hasConfiguration ? (
- <div className="big-padded text-center huge-spacer-bottom authentication-no-config">
- {translate('settings.authentication.github.form.not_configured')}
- </div>
- ) : (
- <>
- <div className="spacer-bottom big-padded bordered display-flex-space-between">
- <div>
- <h5>{appId}</h5>
- <p>{url}</p>
- <p className="big-spacer-top big-spacer-bottom">
- {enabled ? (
- <span className="authentication-enabled spacer-left">
- <CheckIcon className="spacer-right" />
- {translate('settings.authentication.form.enabled')}
- </span>
- ) : (
- translate('settings.authentication.form.not_enabled')
- )}
- </p>
- <Button className="spacer-top" onClick={handleToggleEnable}>
- {enabled
- ? translate('settings.authentication.form.disable')
- : translate('settings.authentication.form.enable')}
- </Button>
- </div>
- <div>
- <Button className="spacer-right" onClick={handleCreateConfiguration}>
- <EditIcon />
- {translate('settings.authentication.form.edit')}
- </Button>
- <Button className="button-red" disabled={enabled} onClick={deleteConfiguration}>
- <DeleteIcon />
- {translate('settings.authentication.form.delete')}
- </Button>
- </div>
- </div>
- <div className="spacer-bottom big-padded bordered display-flex-space-between">
- <form
- onSubmit={async (e) => {
- e.preventDefault();
- if (newGithubProvisioningStatus !== githubProvisioningStatus) {
- setShowConfirmProvisioningModal(true);
- } else {
- await handleSaveGroup();
- }
- }}
- >
- <fieldset className="display-flex-column big-spacer-bottom">
- <label className="h5">
- {translate('settings.authentication.form.provisioning')}
- </label>
-
- {enabled ? (
- <div className="display-flex-row spacer-top">
- <RadioCard
- label={translate(
- 'settings.authentication.github.form.provisioning_with_github'
- )}
- title={translate(
- 'settings.authentication.github.form.provisioning_with_github'
- )}
- selected={newGithubProvisioningStatus ?? githubProvisioningStatus}
- onClick={() => setNewGithubProvisioningStatus(true)}
- disabled={!hasGithubProvisioning}
- >
- {hasGithubProvisioning ? (
- <>
- <p className="spacer-bottom">
- {translate(
- 'settings.authentication.github.form.provisioning_with_github.description'
- )}
- </p>
- <p>
- <FormattedMessage
- id="settings.authentication.github.form.provisioning_with_github.description.doc"
- defaultMessage={translate(
- 'settings.authentication.github.form.provisioning_with_github.description.doc'
- )}
- values={{
- documentation: (
- <DocLink
- to={`/instance-administration/authentication/${
- DOCUMENTATION_LINK_SUFFIXES[AlmKeys.GitHub]
- }/`}
- >
- {translate('documentation')}
- </DocLink>
- ),
- }}
- />
- </p>
- </>
- ) : (
- <p>
- <FormattedMessage
- id="settings.authentication.github.form.provisioning.disabled"
- defaultMessage={translate(
- 'settings.authentication.github.form.provisioning.disabled'
- )}
- values={{
- documentation: (
- // Documentation page not ready yet.
- <DocLink to="/instance-administration/authentication/github">
- {translate('documentation')}
- </DocLink>
- ),
- }}
- />
- </p>
- )}
- </RadioCard>
- <RadioCard
- label={translate('settings.authentication.form.provisioning_at_login')}
- title={translate('settings.authentication.form.provisioning_at_login')}
- selected={!(newGithubProvisioningStatus ?? githubProvisioningStatus)}
- onClick={() => setNewGithubProvisioningStatus(false)}
- >
- {Object.values(values).map((val) => {
- if (!GITHUB_JIT_FIELDS.includes(val.key)) {
- return null;
- }
- return (
- <div key={val.key}>
- <AuthenticationFormField
- settingValue={values[val.key]?.newValue ?? values[val.key]?.value}
- definition={val.definition}
- mandatory={val.mandatory}
- onFieldChange={setNewValue}
- isNotSet={val.isNotSet}
- />
- </div>
- );
- })}
- </RadioCard>
- </div>
- ) : (
- <Alert className="big-spacer-top" variant="info">
- {translate('settings.authentication.github.enable_first')}
- </Alert>
- )}
- </fieldset>
- {enabled && (
- <>
- <SubmitButton disabled={!hasGithubProvisioningConfigChange}>
- {translate('save')}
- </SubmitButton>
- <ResetButtonLink
- className="spacer-left"
- onClick={() => {
- setNewGithubProvisioningStatus(undefined);
- resetJitSetting();
- }}
- disabled={!hasGithubProvisioningConfigChange}
- >
- {translate('cancel')}
- </ResetButtonLink>
- </>
- )}
- {showConfirmProvisioningModal && (
- <ConfirmModal
- onConfirm={() => handleConfirmChangeProvisioning()}
- header={translate(
- 'settings.authentication.github.confirm',
- newGithubProvisioningStatus ? 'auto' : 'jit'
- )}
- onClose={() => setShowConfirmProvisioningModal(false)}
- isDestructive={!newGithubProvisioningStatus}
- confirmButtonText={translate('yes')}
- >
- {translate(
- 'settings.authentication.github.confirm',
- newGithubProvisioningStatus ? 'auto' : 'jit',
- 'description'
- )}
- </ConfirmModal>
- )}
- </form>
- </div>
- </>
- )}
-
- {showEditModal && (
- <ConfigurationForm
- tab={AlmKeys.GitHub}
- excludedField={GITHUB_EXCLUDED_FIELD}
- loading={loading}
- values={values}
- setNewValue={setNewValue}
- canBeSave={canBeSave}
- onClose={handleCancelConfiguration}
- create={!hasConfiguration}
- onReload={reload}
- />
- )}
- </div>
- );
-}
interface SamlAuthenticationProps {
definitions: ExtendedSettingDefinition[];
+ provider: string | undefined;
+ onReload: () => void;
}
export const SAML = 'saml';
const SAML_EXCLUDED_FIELD = [SAML_ENABLED_FIELD, SAML_GROUP_NAME, SAML_SCIM_DEPRECATED];
export default function SamlAuthenticationTab(props: SamlAuthenticationProps) {
- const { definitions } = props;
+ const { definitions, provider, onReload } = props;
const [showEditModal, setShowEditModal] = React.useState(false);
const [showConfirmProvisioningModal, setShowConfirmProvisioningModal] = React.useState(false);
const {
setNewGroupSetting,
reload,
deleteConfiguration,
- } = useSamlConfiguration(definitions);
+ } = useSamlConfiguration(definitions, onReload);
+
+ const hasDifferentProvider = provider !== undefined && provider !== name;
const handleCreateConfiguration = () => {
setShowEditModal(true);
>
<fieldset className="display-flex-column big-spacer-bottom">
<label className="h5">
- {translate('settings.authentication.saml.form.provisioning')}
+ {translate('settings.authentication.form.provisioning')}
</label>
{samlEnabled ? (
<div className="display-flex-row spacer-top">
title={translate('settings.authentication.saml.form.provisioning_with_scim')}
selected={newScimStatus ?? scimStatus}
onClick={() => setNewScimStatus(true)}
- disabled={!hasScim}
+ disabled={!hasScim || hasDifferentProvider}
>
{!hasScim ? (
<p>
</p>
) : (
<>
+ {hasDifferentProvider && (
+ <p className="spacer-bottom text-bold">
+ {translate('settings.authentication.form.other_provisioning_enabled')}
+ </p>
+ )}
<p className="spacer-bottom">
{translate(
'settings.authentication.saml.form.provisioning_with_scim.sub'
import React from 'react';
import { byRole, byText } from 'testing-library-selector';
import AuthenticationServiceMock from '../../../../../api/mocks/AuthenticationServiceMock';
+import SystemServiceMock from '../../../../../api/mocks/SystemServiceMock';
import { AvailableFeaturesContext } from '../../../../../app/components/available-features/AvailableFeaturesContext';
import { definitions } from '../../../../../helpers/mocks/definitions-list';
import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
import Authentication from '../Authentication';
jest.mock('../../../../../api/settings');
+jest.mock('../../../../../api/system');
let handler: AuthenticationServiceMock;
+let system: SystemServiceMock;
beforeEach(() => {
handler = new AuthenticationServiceMock();
+ system = new SystemServiceMock();
});
-afterEach(() => handler.resetValues());
+afterEach(() => {
+ handler.resetValues();
+ system.reset();
+});
const ui = {
saveButton: byRole('button', { name: 'settings.authentication.saml.form.save' }),
editConfigButton: byRole('button', { name: 'settings.authentication.form.edit' }),
enableFirstMessage: byText('settings.authentication.saml.enable_first'),
jitProvisioningButton: byRole('radio', {
- name: 'settings.authentication.form.provisioning_at_login',
+ name: 'settings.authentication.saml.form.provisioning_at_login',
}),
scimProvisioningButton: byRole('radio', {
name: 'settings.authentication.saml.form.provisioning_with_scim',
expect(await saml.saveScim.find()).toBeDisabled();
});
- it('should not allow edtion below Enterprise to select SCIM provisioning', async () => {
+ it('should not allow editions below Enterprise to select SCIM provisioning', async () => {
const { saml } = ui;
const user = userEvent.setup();
definition: ExtendedSettingDefinition;
}
-export default function useGithubConfiguration(definitions: ExtendedSettingDefinition[]) {
+export default function useGithubConfiguration(
+ definitions: ExtendedSettingDefinition[],
+ onReload: () => void
+) {
const config = useConfiguration(definitions, OPTIONAL_FIELDS);
const { values, isValueChange, setNewValue, reload: reloadConfig } = config;
const hasGithubProvisioning = useContext(AvailableFeaturesContext).includes(
const reload = useCallback(async () => {
await reloadConfig();
setGithubProvisioningStatus(await fetchIsGithubProvisioningEnabled());
- }, [reloadConfig]);
+ onReload();
+ }, [reloadConfig, onReload]);
return {
...config,
SAML_SCIM_DEPRECATED,
];
-export default function useSamlConfiguration(definitions: ExtendedSettingDefinition[]) {
+export default function useSamlConfiguration(
+ definitions: ExtendedSettingDefinition[],
+ onReload: () => void
+) {
const [scimStatus, setScimStatus] = React.useState<boolean>(false);
const [newScimStatus, setNewScimStatus] = React.useState<boolean>();
const hasScim = React.useContext(AvailableFeaturesContext).includes(Feature.Scim);
- const {
- loading,
- reload: reloadConfig,
- values,
- setNewValue,
- canBeSave,
- hasConfiguration,
- deleteConfiguration,
- isValueChange,
- } = useConfiguration(definitions, OPTIONAL_FIELDS);
+ const config = useConfiguration(definitions, OPTIONAL_FIELDS);
+ const { reload: reloadConfig, values, setNewValue, isValueChange } = config;
React.useEffect(() => {
(async () => {
const reload = React.useCallback(async () => {
await reloadConfig();
setScimStatus(await fetchIsScimEnabled());
- }, [reloadConfig]);
+ onReload();
+ }, [reloadConfig, onReload]);
return {
+ ...config,
hasScim,
scimStatus,
- loading,
samlEnabled,
name,
url,
groupValue,
- hasConfiguration,
- canBeSave,
values,
setNewValue,
reload,
newScimStatus,
setNewScimStatus,
setNewGroupSetting,
- deleteConfiguration,
};
}
settings.authentication.form.disable=Disable configuration
settings.authentication.form.provisioning=Provisioning
settings.authentication.form.provisioning_at_login=Just-in-Time user and group provisioning (default)
+settings.authentication.form.other_provisioning_enabled=Only one provider can have automatic user and group provisioning.
# GITHUB
settings.authentication.form.create.github=New Github configuration
settings.authentication.saml.form.test.help.incomplete=Some mandatory fields are empty
settings.authentication.saml.form.save_success=Saved successfully
settings.authentication.saml.form.save_partial=Saved partially
+settings.authentication.saml.form.provisioning_at_login=Use this option if your identity provider does not support the SCIM protocol.
settings.authentication.saml.form.provisioning_at_login.sub=Use this option if your identity provider does not support the SCIM protocol.
settings.authentication.saml.form.provisioning_with_scim=Automatic user and group provisioning with SCIM
settings.authentication.saml.form.provisioning_with_scim.sub=Preferred option when using a supported identity provider.