]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22615 Inform admin that updating a group's member when SAML is enabled is temporary
authorguillaume-peoch-sonarsource <guillaume.peoch@sonarsource.com>
Mon, 14 Oct 2024 15:33:32 +0000 (17:33 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 18 Oct 2024 20:03:10 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/groups/__tests__/GroupsApp-it.tsx
server/sonar-web/src/main/js/apps/groups/components/EditMembersModal.tsx
server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 69a3b7d5e14ded2c63194a8437026aa9248910f5..b62a2d7bf8d8cec003267a7053cf6054563778e2 100644 (file)
@@ -25,6 +25,7 @@ import DopTranslationServiceMock from '../../../api/mocks/DopTranslationServiceM
 import GithubProvisioningServiceMock from '../../../api/mocks/GithubProvisioningServiceMock';
 import GroupMembershipsServiceMock from '../../../api/mocks/GroupMembersipsServiceMock';
 import GroupsServiceMock from '../../../api/mocks/GroupsServiceMock';
+import SettingsServiceMock from '../../../api/mocks/SettingsServiceMock';
 import SystemServiceMock from '../../../api/mocks/SystemServiceMock';
 import UsersServiceMock from '../../../api/mocks/UsersServiceMock';
 import { mockGitHubConfiguration } from '../../../helpers/mocks/dop-translation';
@@ -42,6 +43,7 @@ const groupMembershipsHandler = new GroupMembershipsServiceMock();
 const userHandler = new UsersServiceMock(groupMembershipsHandler);
 const dopTranslationHandler = new DopTranslationServiceMock();
 const githubHandler = new GithubProvisioningServiceMock(dopTranslationHandler);
+const settingsHandler = new SettingsServiceMock();
 
 const ui = {
   createGroupButton: byRole('button', { name: 'groups.create_group' }),
@@ -96,6 +98,8 @@ const ui = {
     name: 'local-group local 3',
   }),
 
+  samlWarning: byText('users.update_groups.saml_enabled'),
+
   githubProvisioningPending: byText(/synchronization_pending/),
   githubProvisioningInProgress: byText(/synchronization_in_progress/),
   githubProvisioningSuccess: byText(/synchronization_successful/),
@@ -103,6 +107,7 @@ const ui = {
 };
 
 beforeEach(() => {
+  settingsHandler.reset();
   handler.reset();
   systemHandler.reset();
   dopTranslationHandler.reset();
@@ -275,6 +280,42 @@ describe('in non managed mode', () => {
 
     expect(await screen.findAllByRole('row')).toHaveLength(16);
   });
+
+  it('should display a warning if SAML is enabled', async () => {
+    [
+      {
+        key: 'sonar.auth.saml.signature.enabled',
+        value: 'false',
+      },
+      {
+        key: 'sonar.auth.saml.enabled',
+        value: 'true',
+      },
+      {
+        key: 'sonar.auth.saml.applicationId',
+        value: 'sonarqube',
+      },
+      {
+        key: 'sonar.auth.saml.providerName',
+        value: 'SAML',
+      },
+    ].forEach((setting: any) => settingsHandler.set(setting.key, setting.value));
+
+    const user = userEvent.setup();
+    renderGroupsApp();
+
+    expect(await ui.localGroupRow.find()).toBeInTheDocument();
+
+    await user.click(ui.localGroupEditMembersButton.get());
+
+    expect(await ui.membersDialog.find()).toBeInTheDocument();
+
+    expect(await ui.getMembers()).toHaveLength(3);
+    expect(ui.samlWarning.get()).toBeInTheDocument();
+
+    await user.click(ui.doneButton.get());
+    expect(ui.membersDialog.query()).not.toBeInTheDocument();
+  });
 });
 
 describe('in manage mode', () => {
index bcb718b48a6d76953953fb2ffd041721a9050344..c9c5b88802fd6c7aa66016c1ac57c8d3674c8efa 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { Modal, TextMuted } from 'design-system';
+import { FlagMessage, Modal, TextMuted } from 'design-system';
 import { find } from 'lodash';
 import * as React from 'react';
 import SelectList, {
@@ -25,6 +25,7 @@ import SelectList, {
   SelectListSearchParams,
 } from '../../../components/controls/SelectList';
 import { translate } from '../../../helpers/l10n';
+import { definitions } from '../../../helpers/mocks/definitions-list';
 import {
   useAddGroupMembershipMutation,
   useGroupMembersQuery,
@@ -33,6 +34,10 @@ import {
 } from '../../../queries/group-memberships';
 import { Group } from '../../../types/types';
 import { RestUserBase } from '../../../types/users';
+import useSamlConfiguration from '../../settings/components/authentication/hook/useSamlConfiguration';
+import { SAML } from '../../settings/components/authentication/SamlAuthenticationTab';
+
+const samlDefinitions = definitions.filter((def) => def.subCategory === SAML);
 
 interface Props {
   group: Group;
@@ -54,6 +59,8 @@ export default function EditMembersModal(props: Readonly<Props>) {
   });
   const emptyQueryCache = useRemoveGroupMembersQueryFromCache();
 
+  const { samlEnabled } = useSamlConfiguration(samlDefinitions);
+
   const users: (RestUserBase & { selected?: boolean })[] =
     data?.pages.flatMap((page) => page.users) ?? [];
 
@@ -109,20 +116,27 @@ export default function EditMembersModal(props: Readonly<Props>) {
     <Modal
       headerTitle={modalHeader}
       body={
-        <SelectList
-          elements={users.map((user) => user.id)}
-          elementsTotalCount={data?.pages[0].page.total}
-          needToReload={changedUsers.size > 0 && filter !== SelectListFilter.All}
-          onSearch={onSearch}
-          onSelect={handleSelect}
-          onUnselect={handleUnselect}
-          renderElement={renderElement}
-          selectedElements={users
-            .filter((u) => (changedUsers.has(u.id) ? changedUsers.get(u.id) : u.selected))
-            .map((u) => u.id)}
-          withPaging
-          loading={isLoading}
-        />
+        <>
+          {samlEnabled && (
+            <FlagMessage className="sw-mb-2" variant="warning">
+              {translate('users.update_groups.saml_enabled')}
+            </FlagMessage>
+          )}
+          <SelectList
+            elements={users.map((user) => user.id)}
+            elementsTotalCount={data?.pages[0].page.total}
+            needToReload={changedUsers.size > 0 && filter !== SelectListFilter.All}
+            onSearch={onSearch}
+            onSelect={handleSelect}
+            onUnselect={handleUnselect}
+            renderElement={renderElement}
+            selectedElements={users
+              .filter((u) => (changedUsers.has(u.id) ? changedUsers.get(u.id) : u.selected))
+              .map((u) => u.id)}
+            withPaging
+            loading={isLoading}
+          />
+        </>
       }
       secondaryButtonLabel={translate('done')}
       onClose={props.onClose}
index 2a38f116bbf7d9323fcc7eeec126a64d8427f33a..e0db355c694fc2f9c979dc7e7b08f27e6231b687 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-import { LightPrimary, Modal, Note } from 'design-system';
+import { FlagMessage, LightPrimary, Modal, Note } from 'design-system';
 import { find } from 'lodash';
 import * as React from 'react';
 import SelectList, {
@@ -26,12 +26,17 @@ import SelectList, {
   SelectListSearchParams,
 } from '../../../components/controls/SelectList';
 import { translate } from '../../../helpers/l10n';
+import { definitions } from '../../../helpers/mocks/definitions-list';
 import {
   useAddGroupMembershipMutation,
   useRemoveGroupMembershipMutation,
   useUserGroupsQuery,
 } from '../../../queries/group-memberships';
 import { RestUserDetailed } from '../../../types/users';
+import useSamlConfiguration from '../../settings/components/authentication/hook/useSamlConfiguration';
+import { SAML } from '../../settings/components/authentication/SamlAuthenticationTab';
+
+const samlDefinitions = definitions.filter((def) => def.subCategory === SAML);
 
 interface Props {
   onClose: () => void;
@@ -55,6 +60,8 @@ export default function GroupsForm(props: Props) {
   const { mutateAsync: addUserToGroup } = useAddGroupMembershipMutation();
   const { mutateAsync: removeUserFromGroup } = useRemoveGroupMembershipMutation();
 
+  const { samlEnabled } = useSamlConfiguration(samlDefinitions);
+
   const onSearch = (searchParams: SelectListSearchParams) => {
     if (query === searchParams.query && filter === searchParams.filter) {
       refetch();
@@ -110,6 +117,11 @@ export default function GroupsForm(props: Props) {
       headerTitle={header}
       body={
         <div className="sw-pt-1">
+          {samlEnabled && (
+            <FlagMessage className="sw-mb-2" variant="warning">
+              {translate('users.update_groups.saml_enabled')}
+            </FlagMessage>
+          )}
           <SelectList
             elements={groups?.map((group) => group.id.toString()) ?? []}
             elementsTotalCount={groups?.length}
index 8dc49efc51c7dd5bdb90f3acfa21ddbb4e301aaa..69a8d79f47eb1b494ab4ef3b90731fd67df964ff 100644 (file)
@@ -5521,6 +5521,7 @@ users.last_sonarlint_connection.help_text=The time of the last connection from S
 users.update_users_groups=Update {0}'s group membership
 users.view_users_groups=View {0}'s group membership
 users.update_groups=Update Groups
+users.update_groups.saml_enabled=Updating a group's membership when SAML is enabled will be temporary. The group membership will be updated the next time the user logs in.
 users.view_groups=View Groups
 users.manage_user=Update {0}
 users.update_tokens=Update tokens