From d9803bb1f6e484c7e1c42384b0848952efcb54d5 Mon Sep 17 00:00:00 2001
From: Mathieu Suen
Date: Wed, 8 Mar 2023 16:59:09 +0100
Subject: SONAR-18654 Disable user creation when user auto provisioning is
enable
---
.../src/main/js/api/mocks/UsersServiceMock.ts | 75 ++++++++++++++++++++++
.../project/__tests__/ManualProjectCreate-test.tsx | 2 +-
.../main/js/apps/issues/__tests__/IssuesApp-it.tsx | 8 +--
.../components/__tests__/Definition-it.tsx | 2 +-
server/sonar-web/src/main/js/apps/users/Header.tsx | 72 ++++++++++++---------
.../sonar-web/src/main/js/apps/users/UsersApp.tsx | 18 +++++-
.../main/js/apps/users/__tests__/UsersApp-it.tsx | 56 ++++++++++++++++
.../__tests__/__snapshots__/Header-test.tsx.snap | 12 ++--
.../src/main/js/components/common/Link.tsx | 2 +-
.../js/components/common/__tests__/Link-test.tsx | 6 +-
.../js/components/controls/ValidationInput.tsx | 10 +--
.../__snapshots__/ValidationInput-test.tsx.snap | 4 +-
.../src/main/js/components/icons/Icon.tsx | 9 +--
.../src/main/js/components/icons/QualifierIcon.tsx | 34 +++++-----
.../src/main/js/components/icons/SeverityIcon.tsx | 4 +-
.../issue/components/IssueMessageTags.tsx | 2 +-
.../sonar-web/src/main/js/components/ui/Alert.tsx | 18 ++++--
.../ui/__tests__/__snapshots__/Alert-test.tsx.snap | 12 +++-
server/sonar-web/src/main/js/types/types.ts | 2 +
.../main/resources/org/sonar/l10n/core.properties | 2 +
20 files changed, 261 insertions(+), 89 deletions(-)
create mode 100644 server/sonar-web/src/main/js/api/mocks/UsersServiceMock.ts
create mode 100644 server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx
diff --git a/server/sonar-web/src/main/js/api/mocks/UsersServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/UsersServiceMock.ts
new file mode 100644
index 00000000000..f575559c69a
--- /dev/null
+++ b/server/sonar-web/src/main/js/api/mocks/UsersServiceMock.ts
@@ -0,0 +1,75 @@
+/*
+ * 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 { cloneDeep } from 'lodash';
+import { mockClusterSysInfo, mockIdentityProvider } from '../../helpers/testMocks';
+import { IdentityProvider, Paging, SysInfoCluster } from '../../types/types';
+import { User } from '../../types/users';
+import { getSystemInfo } from '../system';
+import { getIdentityProviders, searchUsers } from '../users';
+
+export default class UsersServiceMock {
+ isManaged = true;
+
+ constructor() {
+ jest.mocked(getSystemInfo).mockImplementation(this.handleGetSystemInfo);
+ jest.mocked(getIdentityProviders).mockImplementation(this.handleGetIdentityProviders);
+ jest.mocked(searchUsers).mockImplementation(this.handleSearchUsers);
+ }
+
+ setIsManaged(managed: boolean) {
+ this.isManaged = managed;
+ }
+
+ handleSearchUsers = (): Promise<{ paging: Paging; users: User[] }> => {
+ return this.reply({
+ paging: {
+ pageIndex: 1,
+ pageSize: 100,
+ total: 0,
+ },
+ users: [],
+ });
+ };
+
+ handleGetIdentityProviders = (): Promise<{ identityProviders: IdentityProvider[] }> => {
+ return this.reply({ identityProviders: [mockIdentityProvider()] });
+ };
+
+ handleGetSystemInfo = (): Promise => {
+ return this.reply(
+ mockClusterSysInfo(
+ this.isManaged
+ ? {
+ System: {
+ 'High Availability': true,
+ 'Server ID': 'asd564-asd54a-5dsfg45',
+ 'External Users and Groups Provisioning': 'Okta',
+ },
+ }
+ : {}
+ )
+ );
+ };
+
+ reply(response: T): Promise {
+ return Promise.resolve(cloneDeep(response));
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx
index 77ed5afb53c..1ac0b03a7f6 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx
@@ -97,7 +97,7 @@ it('should validate form input', async () => {
expect(
screen.getByRole('textbox', { name: 'onboarding.create_project.display_name field_required' })
).toHaveValue('');
- expect(screen.getByLabelText('valid_input')).toBeInTheDocument();
+ expect(screen.getByText('valid_input')).toBeInTheDocument();
expect(
screen.getByText('onboarding.create_project.display_name.error.empty')
).toBeInTheDocument();
diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
index 7999699162f..f1043548c44 100644
--- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
@@ -82,13 +82,13 @@ describe('issues app', () => {
canBrowseAllChildProjects: false,
qualifier: ComponentQualifier.Portfolio,
});
- expect(screen.getByRole('alert', { name: 'alert.tooltip.warning' })).toBeInTheDocument();
+ expect(screen.getByText('issues.not_all_issue_show')).toBeInTheDocument();
await act(async () => {
await user.keyboard('{ArrowRight}');
});
- expect(screen.getByRole('alert', { name: 'alert.tooltip.warning' })).toBeInTheDocument();
+ expect(screen.getByText('issues.not_all_issue_show')).toBeInTheDocument();
});
it('should support OWASP Top 10 version 2021', async () => {
@@ -637,8 +637,8 @@ describe('issues item', () => {
// open severity popup on key press 'i'
await user.keyboard('i');
- expect(screen.getByText('severity.MINOR')).toBeInTheDocument();
- expect(screen.getByText('severity.INFO')).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'severity.MINOR' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'severity.INFO' })).toBeInTheDocument();
// open status popup on key press 'f'
await user.keyboard('f');
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx
index 98d85ea341e..c762837b87c 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx
@@ -56,7 +56,7 @@ const ui = {
fieldsInput: (name: string) => byRole('textbox', { name: `property.${name}.name` }),
savedMsg: byText('settings.state.saved'),
validationMsg: byText(/settings.state.validation_failed/),
- jsonFormatStatus: byRole('status', { name: 'alert.tooltip.info' }),
+ jsonFormatStatus: byText('settings.json.format_error'),
jsonFormatButton: byRole('button', { name: 'settings.json.format' }),
toggleButton: byRole('switch'),
selectOption: (value: string) => byText(value),
diff --git a/server/sonar-web/src/main/js/apps/users/Header.tsx b/server/sonar-web/src/main/js/apps/users/Header.tsx
index 4613b130420..a063aed87cd 100644
--- a/server/sonar-web/src/main/js/apps/users/Header.tsx
+++ b/server/sonar-web/src/main/js/apps/users/Header.tsx
@@ -18,7 +18,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import DocLink from '../../components/common/DocLink';
import { Button } from '../../components/controls/buttons';
+import { Alert } from '../../components/ui/Alert';
import DeferredSpinner from '../../components/ui/DeferredSpinner';
import { translate } from '../../helpers/l10n';
import UserForm from './components/UserForm';
@@ -26,40 +29,49 @@ import UserForm from './components/UserForm';
interface Props {
loading: boolean;
onUpdateUsers: () => void;
+ manageProvider?: string;
}
-interface State {
- openUserForm: boolean;
-}
-
-export default class Header extends React.PureComponent {
- state: State = { openUserForm: false };
-
- handleOpenUserForm = () => {
- this.setState({ openUserForm: true });
- };
-
- handleCloseUserForm = () => {
- this.setState({ openUserForm: false });
- };
+export default function Header(props: Props) {
+ const [openUserForm, setOpenUserForm] = React.useState(false);
- render() {
- return (
-
-
+
`;
diff --git a/server/sonar-web/src/main/js/components/common/Link.tsx b/server/sonar-web/src/main/js/components/common/Link.tsx
index 3ef1206d0d9..e208546b3fb 100644
--- a/server/sonar-web/src/main/js/components/common/Link.tsx
+++ b/server/sonar-web/src/main/js/components/common/Link.tsx
@@ -45,7 +45,7 @@ function Link({ children, size, ...props }: LinkProps, ref: React.ForwardedRef
{anchorProps.target === '_blank' && (
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/Link-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/Link-test.tsx
index e37c4692140..b4078004879 100644
--- a/server/sonar-web/src/main/js/components/common/__tests__/Link-test.tsx
+++ b/server/sonar-web/src/main/js/components/common/__tests__/Link-test.tsx
@@ -35,8 +35,10 @@ it('should correctly render a link that opens in a new window, but is not consid
it('should correctly render an external link', () => {
renderLink({ target: '_blank', to: 'http://example.com' });
- expect(screen.getByRole('link')).toHaveAttribute('rel', 'noopener noreferrer');
- expect(screen.getByLabelText('opens_in_new_window')).toBeInTheDocument();
+ expect(screen.getByRole('link', { name: 'opens_in_new_window click me' })).toHaveAttribute(
+ 'rel',
+ 'noopener noreferrer'
+ );
});
function renderLink(props: Partial = {}) {
diff --git a/server/sonar-web/src/main/js/components/controls/ValidationInput.tsx b/server/sonar-web/src/main/js/components/controls/ValidationInput.tsx
index a853ef3fbef..7ca095822de 100644
--- a/server/sonar-web/src/main/js/components/controls/ValidationInput.tsx
+++ b/server/sonar-web/src/main/js/components/controls/ValidationInput.tsx
@@ -69,10 +69,7 @@ export default function ValidationInput(props: ValidationInputProps) {
<>
{children}
{showValidIcon && isValid && (
-
+
)}
{isInvalid && }
{hasError && {error}}
@@ -83,10 +80,7 @@ export default function ValidationInput(props: ValidationInputProps) {
<>
{children}
{showValidIcon && isValid && (
-
+
)}
{isInvalid &&
}
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ValidationInput-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ValidationInput-test.tsx.snap
index 2233f79b19f..32dd5f73936 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ValidationInput-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ValidationInput-test.tsx.snap
@@ -19,8 +19,8 @@ exports[`should render correctly: default 1`] = `
>
+ {label && !hidden &&
{label}}
{children}
);
diff --git a/server/sonar-web/src/main/js/components/icons/QualifierIcon.tsx b/server/sonar-web/src/main/js/components/icons/QualifierIcon.tsx
index aadcec8dfa6..8c19cef80e5 100644
--- a/server/sonar-web/src/main/js/components/icons/QualifierIcon.tsx
+++ b/server/sonar-web/src/main/js/components/icons/QualifierIcon.tsx
@@ -53,13 +53,13 @@ export default function QualifierIcon({
const FoundIcon = qualifierIcons[qualifier.toLowerCase()];
const ariaLabel = qualifier ? translate(`qualifier.${qualifier}`) : undefined;
return FoundIcon ? (
-
+
) : null;
}
-function ApplicationIcon({ fill, ariaLabel, ...iconProps }: IconProps) {
+function ApplicationIcon({ fill, label: ariaLabel, ...iconProps }: IconProps) {
return (
-
+
+
+
+
+
+
+
+
React.ReactElement> = {
info: InfoSeverityIcon,
};
-export default function SeverityIcon({ severity, ariaLabel, ...iconProps }: Props) {
+export default function SeverityIcon({ severity, ...iconProps }: Omit) {
if (!severity) {
return null;
}
const DesiredIcon = severityIcons[severity.toLowerCase()];
return DesiredIcon ? (
-
+
) : null;
}
diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueMessageTags.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueMessageTags.tsx
index 5f873b242b0..a4ab9133b34 100644
--- a/server/sonar-web/src/main/js/components/issue/components/IssueMessageTags.tsx
+++ b/server/sonar-web/src/main/js/components/issue/components/IssueMessageTags.tsx
@@ -64,7 +64,7 @@ export default function IssueMessageTags(props: IssueMessageTagsProps) {
)}
diff --git a/server/sonar-web/src/main/js/components/ui/Alert.tsx b/server/sonar-web/src/main/js/components/ui/Alert.tsx
index b73db54ab2c..98ac6328b6c 100644
--- a/server/sonar-web/src/main/js/components/ui/Alert.tsx
+++ b/server/sonar-web/src/main/js/components/ui/Alert.tsx
@@ -109,28 +109,37 @@ const StyledAlert = styled.div<{ isInline: boolean; variantInfo: AlertVariantInf
function getAlertVariantInfo(variant: AlertVariant): AlertVariantInformation {
const variantList: Dict = {
error: {
- icon: ,
+ icon: (
+
+ ),
color: colors.alertTextError,
borderColor: colors.alertBorderError,
backGroundColor: colors.alertBackgroundError,
role: 'alert',
},
warning: {
- icon: ,
+ icon: (
+
+ ),
color: colors.alertTextWarning,
borderColor: colors.alertBorderWarning,
backGroundColor: colors.alertBackgroundWarning,
role: 'alert',
},
success: {
- icon: ,
+ icon: (
+
+ ),
color: colors.alertTextSuccess,
borderColor: colors.alertBorderSuccess,
backGroundColor: colors.alertBackgroundSuccess,
role: 'status',
},
info: {
- icon: ,
+ icon: ,
color: colors.alertTextInfo,
borderColor: colors.alertBorderInfo,
backGroundColor: colors.alertBackgroundInfo,
@@ -159,7 +168,6 @@ export function Alert(props: AlertProps & React.HTMLAttributes)
className={classNames('alert', className)}
isInline={isInline}
role={variantInfo.role}
- aria-label={translate('alert.tooltip', variant)}
variantInfo={variantInfo}
{...domProps}
>
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/Alert-test.tsx.snap b/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/Alert-test.tsx.snap
index 9fb48a19931..0d404bb9aa1 100644
--- a/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/Alert-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/Alert-test.tsx.snap
@@ -74,7 +74,6 @@ exports[`should render banner alert with correct css 1`] = `
}
+
+ alert.tooltip.error
+
,
"role": "alert",
}
@@ -140,6 +142,7 @@ exports[`should render properly 1`] = `
"color": "#862422",
"icon":
,
"role": "alert",
}
@@ -147,6 +150,7 @@ exports[`should render properly 1`] = `
>
,
"role": "alert",
}
@@ -177,6 +182,7 @@ exports[`verification of all variants of alert 2`] = `
"color": "#6f4f17",
"icon":
,
"role": "alert",
}
@@ -189,6 +195,7 @@ exports[`verification of all variants of alert 3`] = `
"color": "#215821",
"icon":
,
"role": "status",
}
@@ -201,6 +208,7 @@ exports[`verification of all variants of alert 4`] = `
"color": "#0e516f",
"icon":
,
"role": "status",
}
diff --git a/server/sonar-web/src/main/js/types/types.ts b/server/sonar-web/src/main/js/types/types.ts
index 7c98fcd7bbd..9fbefdbeb01 100644
--- a/server/sonar-web/src/main/js/types/types.ts
+++ b/server/sonar-web/src/main/js/types/types.ts
@@ -229,6 +229,7 @@ export interface IdentityProvider {
iconPath: string;
key: string;
name: string;
+ manage?: boolean;
}
export interface Issue {
@@ -696,6 +697,7 @@ export interface SysInfoCluster extends SysInfoBase {
'High Availability': true;
'Server ID': string;
Version: string;
+ 'External Users and Groups Provisioning'?: string;
};
}
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 a92f62d0183..f0c58f35917 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -4325,6 +4325,8 @@ encryption.how_to_use.content4=For each property that you want to encrypt, gener
#------------------------------------------------------------------------------
users.page=Users
users.page.description=Create and administer individual users.
+users.page.managed_description=Your instance is managed by {provider}. No modification is allowed except for tokens. You can still delete local users. All other operations should be done on your identity provider. See {link} for help managing users.
+users.info=User
users.deactivate=Deactivate
users.deactivate_user=Deactivate User
users.deactivate_user.confirmation=Are you sure you want to deactivate "{0} ({1})"?
--
cgit v1.2.3