aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorguillaume-peoch-sonarsource <guillaume.peoch@sonarsource.com>2023-04-20 12:00:37 +0200
committersonartech <sonartech@sonarsource.com>2023-04-26 20:02:45 +0000
commit7630fae929b43c1c87e514e1f762db1873c71c5e (patch)
treefa15d0be82c28004ae22fa2405285397da7b2691
parentc3f33a9594fa676c90113cd15086acb128e15875 (diff)
downloadsonarqube-7630fae929b43c1c87e514e1f762db1873c71c5e.tar.gz
sonarqube-7630fae929b43c1c87e514e1f762db1873c71c5e.zip
SONAR-19055 SAML Group config is no longer available for Community and Developer editions
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthentication.tsx233
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-it.tsx13
-rw-r--r--server/sonar-web/src/main/js/components/controls/RadioCard.css4
-rw-r--r--server/sonar-web/src/main/js/components/controls/RadioCard.tsx3
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties1
5 files changed, 140 insertions, 114 deletions
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthentication.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthentication.tsx
index 9492328cbd4..e6e123b5f39 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthentication.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/SamlAuthentication.tsx
@@ -28,9 +28,9 @@ import {
} from '../../../../api/settings';
import DocLink from '../../../../components/common/DocLink';
import Link from '../../../../components/common/Link';
-import { Button, ResetButtonLink, SubmitButton } from '../../../../components/controls/buttons';
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';
@@ -40,8 +40,8 @@ import { getBaseUrl } from '../../../../helpers/system';
import { ExtendedSettingDefinition } from '../../../../types/settings';
import { getPropertyName } from '../../utils';
import DefinitionDescription from '../DefinitionDescription';
-import useSamlConfiguration, { SAML_ENABLED_FIELD } from './hook/useLoadSamlSettings';
import SamlConfigurationForm from './SamlConfigurationForm';
+import useSamlConfiguration, { SAML_ENABLED_FIELD } from './hook/useLoadSamlSettings';
interface SamlAuthenticationProps {
definitions: ExtendedSettingDefinition[];
@@ -177,49 +177,36 @@ export default function SamlAuthentication(props: SamlAuthenticationProps) {
</Button>
</div>
</div>
- {hasScim && (
- <div className="spacer-bottom big-padded bordered display-flex-space-between">
- <form
- onSubmit={(e) => {
- e.preventDefault();
- if (newScimStatus !== scimStatus) {
- setShowConfirmProvisioningModal(true);
- } else {
- handleSaveGroup();
- }
- }}
- >
- <fieldset className="display-flex-column big-spacer-bottom">
- <label className="h5">
- {translate('settings.authentication.saml.form.provisioning')}
- </label>
- {samlEnabled ? (
- <div className="display-flex-row spacer-top">
- <RadioCard
- label={translate(
- 'settings.authentication.saml.form.provisioning_with_scim'
- )}
- title={translate(
- 'settings.authentication.saml.form.provisioning_with_scim'
- )}
- selected={newScimStatus ?? scimStatus}
- onClick={() => setNewScimStatus(true)}
- >
- <p className="spacer-bottom">
- {translate(
- 'settings.authentication.saml.form.provisioning_with_scim.sub'
- )}
- </p>
- <p className="spacer-bottom">
- {translate(
- 'settings.authentication.saml.form.provisioning_with_scim.description'
- )}
- </p>
+ <div className="spacer-bottom big-padded bordered display-flex-space-between">
+ <form
+ onSubmit={(e) => {
+ e.preventDefault();
+ if (newScimStatus !== scimStatus) {
+ setShowConfirmProvisioningModal(true);
+ } else {
+ handleSaveGroup();
+ }
+ }}
+ >
+ <fieldset className="display-flex-column big-spacer-bottom">
+ <label className="h5">
+ {translate('settings.authentication.saml.form.provisioning')}
+ </label>
+ {samlEnabled ? (
+ <div className="display-flex-row spacer-top">
+ <RadioCard
+ label={translate('settings.authentication.saml.form.provisioning_with_scim')}
+ title={translate('settings.authentication.saml.form.provisioning_with_scim')}
+ selected={newScimStatus ?? scimStatus}
+ onClick={() => setNewScimStatus(true)}
+ disabled={!hasScim}
+ >
+ {!hasScim ? (
<p>
<FormattedMessage
- id="settings.authentication.saml.form.provisioning_with_scim.description.doc"
+ id="settings.authentication.saml.form.provisioning.disabled"
defaultMessage={translate(
- 'settings.authentication.saml.form.provisioning_with_scim.description.doc'
+ 'settings.authentication.saml.form.provisioning.disabled'
)}
values={{
documentation: (
@@ -230,76 +217,104 @@ export default function SamlAuthentication(props: SamlAuthenticationProps) {
}}
/>
</p>
- </RadioCard>
- <RadioCard
- label={translate('settings.authentication.saml.form.provisioning_at_login')}
- title={translate('settings.authentication.saml.form.provisioning_at_login')}
- selected={!(newScimStatus ?? scimStatus)}
- onClick={() => setNewScimStatus(false)}
- >
- <p>
- {translate('settings.authentication.saml.form.provisioning_at_login.sub')}
- </p>
- {groupValue && (
- <div className="settings-definition">
- <DefinitionDescription definition={groupValue.definition} />
- <div className="settings-definition-right">
- <input
- id={groupValue.definition.key}
- maxLength={4000}
- name={groupValue.definition.key}
- onChange={(e) => setNewGroupSetting(e.currentTarget.value)}
- type="text"
- value={String(groupValue.newValue ?? groupValue.value ?? '')}
- aria-label={getPropertyName(groupValue.definition)}
- />
- </div>
- </div>
- )}
- </RadioCard>
- </div>
- ) : (
- <Alert className="big-spacer-top" variant="info">
- {translate('settings.authentication.saml.enable_first')}
- </Alert>
- )}
- </fieldset>
- {samlEnabled && (
- <>
- <SubmitButton disabled={!hasScimConfigChange}>{translate('save')}</SubmitButton>
- <ResetButtonLink
- className="spacer-left"
- onClick={() => {
- setNewScimStatus(undefined);
- setNewGroupSetting();
- }}
- disabled={!hasScimConfigChange}
+ ) : (
+ <>
+ <p className="spacer-bottom">
+ {translate(
+ 'settings.authentication.saml.form.provisioning_with_scim.sub'
+ )}
+ </p>
+ <p className="spacer-bottom">
+ {translate(
+ 'settings.authentication.saml.form.provisioning_with_scim.description'
+ )}
+ </p>
+ <p>
+ <FormattedMessage
+ id="settings.authentication.saml.form.provisioning_with_scim.description.doc"
+ defaultMessage={translate(
+ 'settings.authentication.saml.form.provisioning_with_scim.description.doc'
+ )}
+ values={{
+ documentation: (
+ <DocLink to="/instance-administration/authentication/saml/scim/overview">
+ {translate('documentation')}
+ </DocLink>
+ ),
+ }}
+ />
+ </p>
+ </>
+ )}
+ </RadioCard>
+ <RadioCard
+ label={translate('settings.authentication.saml.form.provisioning_at_login')}
+ title={translate('settings.authentication.saml.form.provisioning_at_login')}
+ selected={!(newScimStatus ?? scimStatus)}
+ onClick={() => setNewScimStatus(false)}
>
- {translate('cancel')}
- </ResetButtonLink>
- </>
+ <p>
+ {translate('settings.authentication.saml.form.provisioning_at_login.sub')}
+ </p>
+ {groupValue && (
+ <div className="settings-definition">
+ <DefinitionDescription definition={groupValue.definition} />
+ <div className="settings-definition-right">
+ <input
+ id={groupValue.definition.key}
+ maxLength={4000}
+ name={groupValue.definition.key}
+ onChange={(e) => setNewGroupSetting(e.currentTarget.value)}
+ type="text"
+ value={String(groupValue.newValue ?? groupValue.value ?? '')}
+ aria-label={getPropertyName(groupValue.definition)}
+ />
+ </div>
+ </div>
+ )}
+ </RadioCard>
+ </div>
+ ) : (
+ <Alert className="big-spacer-top" variant="info">
+ {translate('settings.authentication.saml.enable_first')}
+ </Alert>
)}
- {showConfirmProvisioningModal && (
- <ConfirmModal
- onConfirm={() => handleConfirmChangeProvisioning()}
- header={translate(
- 'settings.authentication.saml.confirm',
- newScimStatus ? 'scim' : 'jit'
- )}
- onClose={() => setShowConfirmProvisioningModal(false)}
- isDestructive={!newScimStatus}
- confirmButtonText={translate('yes')}
+ </fieldset>
+ {samlEnabled && (
+ <>
+ <SubmitButton disabled={!hasScimConfigChange}>{translate('save')}</SubmitButton>
+ <ResetButtonLink
+ className="spacer-left"
+ onClick={() => {
+ setNewScimStatus(undefined);
+ setNewGroupSetting();
+ }}
+ disabled={!hasScimConfigChange}
>
- {translate(
- 'settings.authentication.saml.confirm',
- newScimStatus ? 'scim' : 'jit',
- 'description'
- )}
- </ConfirmModal>
- )}
- </form>
- </div>
- )}
+ {translate('cancel')}
+ </ResetButtonLink>
+ </>
+ )}
+ {showConfirmProvisioningModal && (
+ <ConfirmModal
+ onConfirm={() => handleConfirmChangeProvisioning()}
+ header={translate(
+ 'settings.authentication.saml.confirm',
+ newScimStatus ? 'scim' : 'jit'
+ )}
+ onClose={() => setShowConfirmProvisioningModal(false)}
+ isDestructive={!newScimStatus}
+ confirmButtonText={translate('yes')}
+ >
+ {translate(
+ 'settings.authentication.saml.confirm',
+ newScimStatus ? 'scim' : 'jit',
+ 'description'
+ )}
+ </ConfirmModal>
+ )}
+ </form>
+ </div>
</>
)}
{showEditModal && (
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-it.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-it.tsx
index 0e597dc7bb3..b8136d361e4 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-it.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-it.tsx
@@ -189,6 +189,19 @@ describe('SAML tab', () => {
expect(await saml.scimProvisioningButton.find()).toBeChecked();
expect(await saml.saveScim.find()).toBeDisabled();
});
+
+ it('should not allow edtion below Enterprise to select SCIM provisioning', async () => {
+ const { saml } = ui;
+ const user = userEvent.setup();
+
+ renderAuthentication();
+
+ await saml.createConfiguration(user);
+ await user.click(await saml.enableConfigButton.find());
+
+ expect(await saml.jitProvisioningButton.find()).toBeChecked();
+ expect(saml.scimProvisioningButton.get()).toHaveAttribute('aria-disabled', 'true');
+ });
});
function renderAuthentication(features: Feature[] = []) {
diff --git a/server/sonar-web/src/main/js/components/controls/RadioCard.css b/server/sonar-web/src/main/js/components/controls/RadioCard.css
index 80bba794114..4f583626f53 100644
--- a/server/sonar-web/src/main/js/components/controls/RadioCard.css
+++ b/server/sonar-web/src/main/js/components/controls/RadioCard.css
@@ -49,10 +49,6 @@
margin-right: 0;
}
-.radio-card:focus {
- outline: none;
-}
-
.radio-card-vertical {
width: 100%;
min-height: auto;
diff --git a/server/sonar-web/src/main/js/components/controls/RadioCard.tsx b/server/sonar-web/src/main/js/components/controls/RadioCard.tsx
index 1e0a7ee30ba..c7241217014 100644
--- a/server/sonar-web/src/main/js/components/controls/RadioCard.tsx
+++ b/server/sonar-web/src/main/js/components/controls/RadioCard.tsx
@@ -71,7 +71,8 @@ export default function RadioCard(props: Props) {
onClick={isActionable && !disabled ? onClick : undefined}
role="radio"
aria-label={label}
- tabIndex={0}
+ aria-disabled={disabled}
+ tabIndex={disabled ? -1 : 0}
>
<h2 className="radio-card-header big-spacer-bottom">
<span className="display-flex-center link-radio">
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index aae9c18cab7..87e48c71bf9 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -1369,6 +1369,7 @@ settings.authentication.saml.form.provisioning_with_scim=Automatic user and grou
settings.authentication.saml.form.provisioning_with_scim.sub=Preferred option when using a supported identity provider.
settings.authentication.saml.form.provisioning_with_scim.description=Users and groups are automatically provisioned from your identity provider using the SCIM protocol. Once activated, managed users and groups can only be modified from your identity provider. Existing local users and groups will be kept.
settings.authentication.saml.form.provisioning_with_scim.description.doc=For a list of supported providers and more details on automatic provisioning, see {documentation}.
+settings.authentication.saml.form.provisioning.disabled=Your current edition does not support provisioning with SCIM. See the {documentation} for more information.
settings.authentication.saml.enable_first=Enable your SAML configuration to benefit from automatic user provisioning options.
settings.pr_decoration.binding.category=DevOps Platform Integration
settings.pr_decoration.binding.no_bindings=A system administrator needs to enable this feature in the global settings.