From b46f4fec4986b5d870d668658a8d98fd62aa14ff Mon Sep 17 00:00:00 2001 From: guillaume-peoch-sonarsource Date: Mon, 14 Oct 2024 17:33:32 +0200 Subject: [PATCH] SONAR-22615 Inform admin that updating a group's member when SAML is enabled is temporary --- .../js/apps/groups/__tests__/GroupsApp-it.tsx | 41 +++++++++++++++++ .../groups/components/EditMembersModal.tsx | 44 ++++++++++++------- .../js/apps/users/components/GroupsForm.tsx | 14 +++++- .../resources/org/sonar/l10n/core.properties | 1 + 4 files changed, 84 insertions(+), 16 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/groups/__tests__/GroupsApp-it.tsx b/server/sonar-web/src/main/js/apps/groups/__tests__/GroupsApp-it.tsx index 69a3b7d5e14..b62a2d7bf8d 100644 --- a/server/sonar-web/src/main/js/apps/groups/__tests__/GroupsApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/groups/__tests__/GroupsApp-it.tsx @@ -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', () => { diff --git a/server/sonar-web/src/main/js/apps/groups/components/EditMembersModal.tsx b/server/sonar-web/src/main/js/apps/groups/components/EditMembersModal.tsx index bcb718b48a6..c9c5b88802f 100644 --- a/server/sonar-web/src/main/js/apps/groups/components/EditMembersModal.tsx +++ b/server/sonar-web/src/main/js/apps/groups/components/EditMembersModal.tsx @@ -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) { }); 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) { 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 && ( + + {translate('users.update_groups.saml_enabled')} + + )} + 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} diff --git a/server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx b/server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx index 2a38f116bbf..e0db355c694 100644 --- a/server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx @@ -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={
+ {samlEnabled && ( + + {translate('users.update_groups.saml_enabled')} + + )} group.id.toString()) ?? []} elementsTotalCount={groups?.length} 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 8dc49efc51c..69a8d79f47e 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -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 -- 2.39.5