aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/users
diff options
context:
space:
mode:
authorMathieu Suen <mathieu.suen@sonarsource.com>2023-03-08 16:59:09 +0100
committersonartech <sonartech@sonarsource.com>2023-03-22 20:04:07 +0000
commitd9803bb1f6e484c7e1c42384b0848952efcb54d5 (patch)
treeea9bdf92ec8e3b195128eb0b7b16c7e5f736c461 /server/sonar-web/src/main/js/apps/users
parent9d02b0639e617458d771c4a794db33e70cbb35a1 (diff)
downloadsonarqube-d9803bb1f6e484c7e1c42384b0848952efcb54d5.tar.gz
sonarqube-d9803bb1f6e484c7e1c42384b0848952efcb54d5.zip
SONAR-18654 Disable user creation when user auto provisioning is enable
Diffstat (limited to 'server/sonar-web/src/main/js/apps/users')
-rw-r--r--server/sonar-web/src/main/js/apps/users/Header.tsx72
-rw-r--r--server/sonar-web/src/main/js/apps/users/UsersApp.tsx18
-rw-r--r--server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx56
-rw-r--r--server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/Header-test.tsx.snap12
4 files changed, 119 insertions, 39 deletions
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<Props, State> {
- 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 (
- <header className="page-header" id="users-header">
- <h1 className="page-title">{translate('users.page')}</h1>
- <DeferredSpinner loading={this.props.loading} />
+ const { manageProvider, loading } = props;
+ return (
+ <div className="page-header null-spacer-bottom">
+ <h2 className="page-title">{translate('users.page')}</h2>
+ <DeferredSpinner loading={loading} />
- <div className="page-actions">
- <Button id="users-create" onClick={this.handleOpenUserForm}>
- {translate('users.create_user')}
- </Button>
- </div>
+ <div className="page-actions">
+ <Button
+ id="users-create"
+ disabled={manageProvider !== undefined}
+ onClick={() => setOpenUserForm(true)}
+ >
+ {translate('users.create_user')}
+ </Button>
+ </div>
+ {manageProvider === undefined ? (
<p className="page-description">{translate('users.page.description')}</p>
- {this.state.openUserForm && (
- <UserForm onClose={this.handleCloseUserForm} onUpdateUsers={this.props.onUpdateUsers} />
- )}
- </header>
- );
- }
+ ) : (
+ <Alert className="page-description max-width-100" variant="info">
+ <FormattedMessage
+ defaultMessage={translate('users.page.managed_description')}
+ id="users.page.managed_description"
+ values={{
+ provider: manageProvider,
+ link: (
+ <DocLink to="/instance-administration/authentication/overview/">
+ {translate('documentation')}
+ </DocLink>
+ ),
+ }}
+ />
+ </Alert>
+ )}
+ {openUserForm && (
+ <UserForm onClose={() => setOpenUserForm(false)} onUpdateUsers={props.onUpdateUsers} />
+ )}
+ </div>
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/users/UsersApp.tsx b/server/sonar-web/src/main/js/apps/users/UsersApp.tsx
index b0d7c36df4f..768b2c8f7e6 100644
--- a/server/sonar-web/src/main/js/apps/users/UsersApp.tsx
+++ b/server/sonar-web/src/main/js/apps/users/UsersApp.tsx
@@ -19,13 +19,14 @@
*/
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
+import { getSystemInfo } from '../../api/system';
import { getIdentityProviders, searchUsers } from '../../api/users';
import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
import ListFooter from '../../components/controls/ListFooter';
import Suggestions from '../../components/embed-docs-modal/Suggestions';
import { Location, Router, withRouter } from '../../components/hoc/withRouter';
import { translate } from '../../helpers/l10n';
-import { IdentityProvider, Paging } from '../../types/types';
+import { IdentityProvider, Paging, SysInfoCluster } from '../../types/types';
import { CurrentUser, User } from '../../types/users';
import Header from './Header';
import Search from './Search';
@@ -40,6 +41,7 @@ interface Props {
interface State {
identityProviders: IdentityProvider[];
+ manageProvider?: string;
loading: boolean;
paging?: Paging;
users: User[];
@@ -52,6 +54,7 @@ export class UsersApp extends React.PureComponent<Props, State> {
componentDidMount() {
this.mounted = true;
this.fetchIdentityProviders();
+ this.fetchManageInstance();
this.fetchUsers();
}
@@ -71,6 +74,15 @@ export class UsersApp extends React.PureComponent<Props, State> {
}
};
+ async fetchManageInstance() {
+ const info = (await getSystemInfo()) as SysInfoCluster;
+ if (this.mounted) {
+ this.setState({
+ manageProvider: info.System['External Users and Groups Provisioning'],
+ });
+ }
+ }
+
fetchIdentityProviders = () =>
getIdentityProviders().then(({ identityProviders }) => {
if (this.mounted) {
@@ -116,12 +128,12 @@ export class UsersApp extends React.PureComponent<Props, State> {
render() {
const query = parseQuery(this.props.location.query);
- const { loading, paging, users } = this.state;
+ const { loading, paging, users, manageProvider } = this.state;
return (
<main className="page page-limited" id="users-page">
<Suggestions suggestions="users" />
<Helmet defer={false} title={translate('users.page')} />
- <Header loading={loading} onUpdateUsers={this.fetchUsers} />
+ <Header loading={loading} onUpdateUsers={this.fetchUsers} manageProvider={manageProvider} />
<Search query={query} updateQuery={this.updateQuery} />
<UsersList
currentUser={this.props.currentUser}
diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx
new file mode 100644
index 00000000000..3917a11296e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-it.tsx
@@ -0,0 +1,56 @@
+/*
+ * 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 * as React from 'react';
+import { byRole, byText } from 'testing-library-selector';
+import UsersServiceMock from '../../../api/mocks/UsersServiceMock';
+import { renderApp } from '../../../helpers/testReactTestingUtils';
+import UsersApp from '../UsersApp';
+
+jest.mock('../../../api/users');
+jest.mock('../../../api/system');
+
+const handler = new UsersServiceMock();
+
+const ui = {
+ createUserButton: byRole('button', { name: 'users.create_user' }),
+ infoManageMode: byText(/users\.page\.managed_description/),
+ description: byText('users.page.description'),
+};
+
+it('should render list of user in non manage mode', async () => {
+ handler.setIsManaged(false);
+ renderUsersApp();
+
+ expect(await ui.description.find()).toBeInTheDocument();
+ expect(ui.createUserButton.get()).toBeEnabled();
+});
+
+it('should render list of user in manage mode', async () => {
+ handler.setIsManaged(true);
+ renderUsersApp();
+
+ expect(await ui.infoManageMode.find()).toBeInTheDocument();
+ expect(ui.createUserButton.get()).toBeDisabled();
+});
+
+function renderUsersApp() {
+ renderApp('admin/users', <UsersApp />);
+}
diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/Header-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/Header-test.tsx.snap
index e09d8ad8088..1e3f25b280f 100644
--- a/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/Header-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/Header-test.tsx.snap
@@ -1,15 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render correctly 1`] = `
-<header
- className="page-header"
- id="users-header"
+<div
+ className="page-header null-spacer-bottom"
>
- <h1
+ <h2
className="page-title"
>
users.page
- </h1>
+ </h2>
<DeferredSpinner
loading={true}
/>
@@ -17,6 +16,7 @@ exports[`should render correctly 1`] = `
className="page-actions"
>
<Button
+ disabled={false}
id="users-create"
onClick={[Function]}
>
@@ -28,5 +28,5 @@ exports[`should render correctly 1`] = `
>
users.page.description
</p>
-</header>
+</div>
`;