From d62ea2bea95813ce8d0c18143a4147f83414b23c Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Tue, 22 Jun 2021 13:04:44 -0500 Subject: [PATCH 01/21] Rename method to close both flyouts This is shared with the forthcoming user flyouts closeRoleMappingFlyout -> closeUsersAndRolesFlyout --- .../components/role_mappings/role_mapping.tsx | 4 ++-- .../role_mappings/role_mappings_logic.test.ts | 4 ++-- .../role_mappings/role_mappings_logic.ts | 16 ++++++++-------- .../role_mapping/role_mapping_flyout.test.tsx | 4 ++-- .../shared/role_mapping/role_mapping_flyout.tsx | 10 ++++++---- .../views/role_mappings/role_mapping.tsx | 4 ++-- .../role_mappings/role_mappings_logic.test.ts | 4 ++-- .../views/role_mappings/role_mappings_logic.ts | 16 ++++++++-------- 8 files changed, 32 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mapping.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mapping.tsx index b6a9dd72cfd05..dbebd8e46a219 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mapping.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mapping.tsx @@ -28,7 +28,7 @@ export const RoleMapping: React.FC = () => { handleAuthProviderChange, handleRoleChange, handleSaveMapping, - closeRoleMappingFlyout, + closeUsersAndRolesFlyout, } = useActions(RoleMappingsLogic); const { @@ -68,7 +68,7 @@ export const RoleMapping: React.FC = () => { 0} error={roleMappingErrors}> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts index 6985f213d1dd5..bf684d5a0446c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts @@ -260,12 +260,12 @@ describe('RoleMappingsLogic', () => { expect(clearFlashMessages).toHaveBeenCalled(); }); - it('closeRoleMappingFlyout', () => { + it('closeUsersAndRolesFlyout', () => { mount({ ...mappingsServerProps, roleMappingFlyoutOpen: true, }); - RoleMappingsLogic.actions.closeRoleMappingFlyout(); + RoleMappingsLogic.actions.closeUsersAndRolesFlyout(); expect(RoleMappingsLogic.values.roleMappingFlyoutOpen).toEqual(false); expect(clearFlashMessages).toHaveBeenCalled(); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts index e2ef75897528c..c36fb89639bb9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts @@ -65,7 +65,7 @@ interface RoleMappingsActions { }): { roleMappings: ASRoleMapping[] }; setRoleMappingsData(data: RoleMappingsServerDetails): RoleMappingsServerDetails; openRoleMappingFlyout(): void; - closeRoleMappingFlyout(): void; + closeUsersAndRolesFlyout(): void; setRoleMappingErrors(errors: string[]): { errors: string[] }; enableRoleBasedAccess(): void; } @@ -114,7 +114,7 @@ export const RoleMappingsLogic = kea ({ roleMappingId }), handleSaveMapping: true, openRoleMappingFlyout: true, - closeRoleMappingFlyout: false, + closeUsersAndRolesFlyout: false, }, reducers: { dataLoading: [ @@ -172,7 +172,7 @@ export const RoleMappingsLogic = kea roleMapping, resetState: () => null, - closeRoleMappingFlyout: () => null, + closeUsersAndRolesFlyout: () => null, }, ], roleType: [ @@ -198,7 +198,7 @@ export const RoleMappingsLogic = kea value, resetState: () => '', - closeRoleMappingFlyout: () => '', + closeUsersAndRolesFlyout: () => '', }, ], attributeName: [ @@ -207,7 +207,7 @@ export const RoleMappingsLogic = kea getFirstAttributeName(roleMapping), handleAttributeSelectorChange: (_, { value }) => value, resetState: () => 'username', - closeRoleMappingFlyout: () => 'username', + closeUsersAndRolesFlyout: () => 'username', }, ], selectedEngines: [ @@ -251,7 +251,7 @@ export const RoleMappingsLogic = kea true, - closeRoleMappingFlyout: () => false, + closeUsersAndRolesFlyout: () => false, initializeRoleMappings: () => false, initializeRoleMapping: () => true, }, @@ -261,7 +261,7 @@ export const RoleMappingsLogic = kea errors, handleSaveMapping: () => [], - closeRoleMappingFlyout: () => [], + closeUsersAndRolesFlyout: () => [], }, ], }, @@ -357,7 +357,7 @@ export const RoleMappingsLogic = kea { clearFlashMessages(); }, - closeRoleMappingFlyout: () => { + closeUsersAndRolesFlyout: () => { clearFlashMessages(); }, openRoleMappingFlyout: () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mapping_flyout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mapping_flyout.test.tsx index c0973bb2c9504..ffcf5508233fc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mapping_flyout.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mapping_flyout.test.tsx @@ -20,13 +20,13 @@ import { import { RoleMappingFlyout } from './role_mapping_flyout'; describe('RoleMappingFlyout', () => { - const closeRoleMappingFlyout = jest.fn(); + const closeUsersAndRolesFlyout = jest.fn(); const handleSaveMapping = jest.fn(); const props = { isNew: true, disabled: false, - closeRoleMappingFlyout, + closeUsersAndRolesFlyout, handleSaveMapping, }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mapping_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mapping_flyout.tsx index bae991fef3655..4416a2de28011 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mapping_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mapping_flyout.tsx @@ -36,7 +36,7 @@ interface Props { children: React.ReactNode; isNew: boolean; disabled: boolean; - closeRoleMappingFlyout(): void; + closeUsersAndRolesFlyout(): void; handleSaveMapping(): void; } @@ -44,13 +44,13 @@ export const RoleMappingFlyout: React.FC = ({ children, isNew, disabled, - closeRoleMappingFlyout, + closeUsersAndRolesFlyout, handleSaveMapping, }) => ( @@ -71,7 +71,9 @@ export const RoleMappingFlyout: React.FC = ({ - {CANCEL_BUTTON_LABEL} + + {CANCEL_BUTTON_LABEL} + { handleAttributeSelectorChange, handleRoleChange, handleAuthProviderChange, - closeRoleMappingFlyout, + closeUsersAndRolesFlyout, } = useActions(RoleMappingsLogic); const { @@ -70,7 +70,7 @@ export const RoleMapping: React.FC = () => { 0} error={roleMappingErrors}> diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts index a4bbddbd23b49..ae9d3da060fe1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts @@ -228,12 +228,12 @@ describe('RoleMappingsLogic', () => { expect(clearFlashMessages).toHaveBeenCalled(); }); - it('closeRoleMappingFlyout', () => { + it('closeUsersAndRolesFlyout', () => { mount({ ...mappingsServerProps, roleMappingFlyoutOpen: true, }); - RoleMappingsLogic.actions.closeRoleMappingFlyout(); + RoleMappingsLogic.actions.closeUsersAndRolesFlyout(); expect(RoleMappingsLogic.values.roleMappingFlyoutOpen).toEqual(false); expect(clearFlashMessages).toHaveBeenCalled(); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts index 76b41b2f383eb..4b4b38aff957d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts @@ -63,7 +63,7 @@ interface RoleMappingsActions { }): { roleMappings: WSRoleMapping[] }; setRoleMappingsData(data: RoleMappingsServerDetails): RoleMappingsServerDetails; openRoleMappingFlyout(): void; - closeRoleMappingFlyout(): void; + closeUsersAndRolesFlyout(): void; setRoleMappingErrors(errors: string[]): { errors: string[] }; enableRoleBasedAccess(): void; } @@ -111,7 +111,7 @@ export const RoleMappingsLogic = kea ({ roleMappingId }), handleSaveMapping: true, openRoleMappingFlyout: true, - closeRoleMappingFlyout: false, + closeUsersAndRolesFlyout: false, }, reducers: { dataLoading: [ @@ -161,7 +161,7 @@ export const RoleMappingsLogic = kea roleMapping, resetState: () => null, - closeRoleMappingFlyout: () => null, + closeUsersAndRolesFlyout: () => null, }, ], roleType: [ @@ -186,7 +186,7 @@ export const RoleMappingsLogic = kea value, resetState: () => '', - closeRoleMappingFlyout: () => '', + closeUsersAndRolesFlyout: () => '', }, ], attributeName: [ @@ -195,7 +195,7 @@ export const RoleMappingsLogic = kea getFirstAttributeName(roleMapping), handleAttributeSelectorChange: (_, { value }) => value, resetState: () => 'username', - closeRoleMappingFlyout: () => 'username', + closeUsersAndRolesFlyout: () => 'username', }, ], selectedGroups: [ @@ -244,7 +244,7 @@ export const RoleMappingsLogic = kea true, - closeRoleMappingFlyout: () => false, + closeUsersAndRolesFlyout: () => false, initializeRoleMappings: () => false, initializeRoleMapping: () => true, }, @@ -254,7 +254,7 @@ export const RoleMappingsLogic = kea errors, handleSaveMapping: () => [], - closeRoleMappingFlyout: () => [], + closeUsersAndRolesFlyout: () => [], }, ], }, @@ -349,7 +349,7 @@ export const RoleMappingsLogic = kea { clearFlashMessages(); }, - closeRoleMappingFlyout: () => { + closeUsersAndRolesFlyout: () => { clearFlashMessages(); }, openRoleMappingFlyout: () => { From 1a6cf9d966a8835b1f5f43d813afdec5cce82f7f Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Tue, 22 Jun 2021 13:40:35 -0500 Subject: [PATCH 02/21] Add logic for elasticsearch users and single user role mappings --- .../role_mappings/role_mappings_logic.test.ts | 82 ++++++++++++- .../role_mappings/role_mappings_logic.ts | 58 ++++++++- .../role_mappings/role_mappings_logic.test.ts | 113 +++++++++++++++--- .../role_mappings/role_mappings_logic.ts | 58 ++++++++- 4 files changed, 291 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts index bf684d5a0446c..c3261fcc39aec 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts @@ -15,11 +15,18 @@ import { engines } from '../../__mocks__/engines.mock'; import { nextTick } from '@kbn/test/jest'; -import { asRoleMapping } from '../../../shared/role_mapping/__mocks__/roles'; +import { elasticsearchUsers } from '../../../shared/role_mapping/__mocks__/elasticsearchUsers'; + +import { + asRoleMapping, + asSingleUserRoleMapping, +} from '../../../shared/role_mapping/__mocks__/roles'; import { ANY_AUTH_PROVIDER } from '../../../shared/role_mapping/constants'; import { RoleMappingsLogic } from './role_mappings_logic'; +const emptyUser = { username: '', email: '' }; + describe('RoleMappingsLogic', () => { const { http } = mockHttpValues; const { clearFlashMessages, flashAPIErrors, setSuccessMessage } = mockFlashMessageHelpers; @@ -28,6 +35,8 @@ describe('RoleMappingsLogic', () => { attributes: [], availableAuthProviders: [], elasticsearchRoles: [], + elasticsearchUser: emptyUser, + elasticsearchUsers: [], roleMapping: null, roleMappingFlyoutOpen: false, roleMappings: [], @@ -43,6 +52,8 @@ describe('RoleMappingsLogic', () => { selectedAuthProviders: [ANY_AUTH_PROVIDER], selectedOptions: [], roleMappingErrors: [], + singleUserRoleMapping: null, + singleUserRoleMappings: [], }; const mappingsServerProps = { @@ -53,6 +64,8 @@ describe('RoleMappingsLogic', () => { availableEngines: engines, elasticsearchRoles: [], hasAdvancedRoles: false, + singleUserRoleMappings: [asSingleUserRoleMapping], + elasticsearchUsers, }; beforeEach(() => { @@ -83,7 +96,19 @@ describe('RoleMappingsLogic', () => { elasticsearchRoles: mappingsServerProps.elasticsearchRoles, selectedEngines: new Set(), selectedOptions: [], + elasticsearchUsers, + elasticsearchUser: elasticsearchUsers[0], + singleUserRoleMappings: [asSingleUserRoleMapping], + }); + }); + + it('handles fallback if no elasticsearch users present', () => { + RoleMappingsLogic.actions.setRoleMappingsData({ + ...mappingsServerProps, + elasticsearchUsers: [], }); + + expect(RoleMappingsLogic.values.elasticsearchUser).toEqual(emptyUser); }); }); @@ -94,6 +119,26 @@ describe('RoleMappingsLogic', () => { expect(RoleMappingsLogic.values.dataLoading).toEqual(false); }); + describe('setElasticsearchUser', () => { + it('sets user', () => { + RoleMappingsLogic.actions.setElasticsearchUser(elasticsearchUsers[0]); + + expect(RoleMappingsLogic.values.elasticsearchUser).toEqual(elasticsearchUsers[0]); + }); + + it('handles fallback if no user present', () => { + RoleMappingsLogic.actions.setElasticsearchUser(undefined); + + expect(RoleMappingsLogic.values.elasticsearchUser).toEqual(emptyUser); + }); + }); + + it('setSingleUserRoleMapping', () => { + RoleMappingsLogic.actions.setSingleUserRoleMapping(asSingleUserRoleMapping); + + expect(RoleMappingsLogic.values.singleUserRoleMapping).toEqual(asSingleUserRoleMapping); + }); + it('handleRoleChange', () => { RoleMappingsLogic.actions.handleRoleChange('dev'); @@ -174,6 +219,8 @@ describe('RoleMappingsLogic', () => { attributeName: 'role', elasticsearchRoles, selectedEngines: new Set(), + elasticsearchUsers, + singleUserRoleMappings: [asSingleUserRoleMapping], }); }); @@ -335,6 +382,39 @@ describe('RoleMappingsLogic', () => { }); }); + describe('initializeSingleUserRoleMapping', () => { + let setElasticsearchUserSpy: jest.MockedFunction; + let setRoleMappingSpy: jest.MockedFunction; + let setSingleUserRoleMappingSpy: jest.MockedFunction; + beforeEach(() => { + setElasticsearchUserSpy = jest.spyOn(RoleMappingsLogic.actions, 'setElasticsearchUser'); + setRoleMappingSpy = jest.spyOn(RoleMappingsLogic.actions, 'setRoleMapping'); + setSingleUserRoleMappingSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setSingleUserRoleMapping' + ); + }); + + it('should handle the new user state and only set an empty mapping', () => { + RoleMappingsLogic.actions.initializeSingleUserRoleMapping(); + + expect(setElasticsearchUserSpy).not.toHaveBeenCalled(); + expect(setRoleMappingSpy).not.toHaveBeenCalled(); + expect(setSingleUserRoleMappingSpy).toHaveBeenCalledWith(undefined); + }); + + it('should handle an existing user state and set mapping', () => { + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + RoleMappingsLogic.actions.initializeSingleUserRoleMapping( + asSingleUserRoleMapping.roleMapping.id + ); + + expect(setElasticsearchUserSpy).toHaveBeenCalled(); + expect(setRoleMappingSpy).toHaveBeenCalled(); + expect(setSingleUserRoleMappingSpy).toHaveBeenCalledWith(asSingleUserRoleMapping); + }); + }); + describe('handleSaveMapping', () => { const body = { roleType: 'owner', diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts index c36fb89639bb9..65ceb0051ec21 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts @@ -16,7 +16,7 @@ import { } from '../../../shared/flash_messages'; import { HttpLogic } from '../../../shared/http'; import { ANY_AUTH_PROVIDER } from '../../../shared/role_mapping/constants'; -import { AttributeName } from '../../../shared/types'; +import { AttributeName, SingleUserRoleMapping, ElasticsearchUser } from '../../../shared/types'; import { ASRoleMapping, RoleTypes } from '../../types'; import { roleHasScopedEngines } from '../../utils/role/has_scoped_engines'; import { Engine } from '../engine/types'; @@ -27,20 +27,25 @@ import { ROLE_MAPPING_UPDATED_MESSAGE, } from './constants'; +type UserMapping = SingleUserRoleMapping; + interface RoleMappingsServerDetails { roleMappings: ASRoleMapping[]; attributes: string[]; authProviders: string[]; availableEngines: Engine[]; elasticsearchRoles: string[]; + elasticsearchUsers: ElasticsearchUser[]; hasAdvancedRoles: boolean; multipleAuthProvidersConfig: boolean; + singleUserRoleMappings: UserMapping[]; } const getFirstAttributeName = (roleMapping: ASRoleMapping) => Object.entries(roleMapping.rules)[0][0] as AttributeName; const getFirstAttributeValue = (roleMapping: ASRoleMapping) => Object.entries(roleMapping.rules)[0][1] as AttributeName; +const emptyUser = { username: '', email: '' } as ElasticsearchUser; interface RoleMappingsActions { handleAccessAllEnginesChange(selected: boolean): { selected: boolean }; @@ -55,15 +60,20 @@ interface RoleMappingsActions { handleRoleChange(roleType: RoleTypes): { roleType: RoleTypes }; handleSaveMapping(): void; initializeRoleMapping(roleMappingId?: string): { roleMappingId?: string }; + initializeSingleUserRoleMapping(roleMappingId?: string): { roleMappingId?: string }; initializeRoleMappings(): void; resetState(): void; setRoleMapping(roleMapping: ASRoleMapping): { roleMapping: ASRoleMapping }; + setSingleUserRoleMapping(data?: UserMapping): { singleUserRoleMapping: UserMapping }; setRoleMappings({ roleMappings, }: { roleMappings: ASRoleMapping[]; }): { roleMappings: ASRoleMapping[] }; setRoleMappingsData(data: RoleMappingsServerDetails): RoleMappingsServerDetails; + setElasticsearchUser( + elasticsearchUser?: ElasticsearchUser + ): { elasticsearchUser: ElasticsearchUser }; openRoleMappingFlyout(): void; closeUsersAndRolesFlyout(): void; setRoleMappingErrors(errors: string[]): { errors: string[] }; @@ -79,10 +89,14 @@ interface RoleMappingsValues { availableEngines: Engine[]; dataLoading: boolean; elasticsearchRoles: string[]; + elasticsearchUsers: ElasticsearchUser[]; + elasticsearchUser: ElasticsearchUser; hasAdvancedRoles: boolean; multipleAuthProvidersConfig: boolean; roleMapping: ASRoleMapping | null; roleMappings: ASRoleMapping[]; + singleUserRoleMapping: UserMapping | null; + singleUserRoleMappings: UserMapping[]; roleType: RoleTypes; selectedAuthProviders: string[]; selectedEngines: Set; @@ -96,6 +110,8 @@ export const RoleMappingsLogic = kea data, setRoleMapping: (roleMapping: ASRoleMapping) => ({ roleMapping }), + setElasticsearchUser: (elasticsearchUser: ElasticsearchUser) => ({ elasticsearchUser }), + setSingleUserRoleMapping: (singleUserRoleMapping: UserMapping) => ({ singleUserRoleMapping }), setRoleMappings: ({ roleMappings }: { roleMappings: ASRoleMapping[] }) => ({ roleMappings }), setRoleMappingErrors: (errors: string[]) => ({ errors }), handleAuthProviderChange: (value: string) => ({ value }), @@ -110,6 +126,7 @@ export const RoleMappingsLogic = kea ({ roleMappingId }), initializeRoleMapping: (roleMappingId) => ({ roleMappingId }), handleDeleteMapping: (roleMappingId: string) => ({ roleMappingId }), handleSaveMapping: true, @@ -134,6 +151,13 @@ export const RoleMappingsLogic = kea [], }, ], + singleUserRoleMappings: [ + [], + { + setRoleMappingsData: (_, { singleUserRoleMappings }) => singleUserRoleMappings, + resetState: () => [], + }, + ], multipleAuthProvidersConfig: [ false, { @@ -167,6 +191,13 @@ export const RoleMappingsLogic = kea elasticsearchRoles, }, ], + elasticsearchUsers: [ + [], + { + setRoleMappingsData: (_, { elasticsearchUsers }) => elasticsearchUsers, + resetState: () => [], + }, + ], roleMapping: [ null, { @@ -256,6 +287,13 @@ export const RoleMappingsLogic = kea true, }, ], + singleUserRoleMapping: [ + null, + { + setSingleUserRoleMapping: (_, { singleUserRoleMapping }) => singleUserRoleMapping || null, + closeUsersAndRolesFlyout: () => null, + }, + ], roleMappingErrors: [ [], { @@ -264,6 +302,14 @@ export const RoleMappingsLogic = kea [], }, ], + elasticsearchUser: [ + emptyUser, + { + setRoleMappingsData: (_, { elasticsearchUsers }) => elasticsearchUsers[0] || emptyUser, + setElasticsearchUser: (_, { elasticsearchUser }) => elasticsearchUser || emptyUser, + closeUsersAndRolesFlyout: () => emptyUser, + }, + ], }, selectors: ({ selectors }) => ({ selectedOptions: [ @@ -303,6 +349,16 @@ export const RoleMappingsLogic = kea id === roleMappingId); if (roleMapping) actions.setRoleMapping(roleMapping); }, + initializeSingleUserRoleMapping: ({ roleMappingId }) => { + const singleUserRoleMapping = values.singleUserRoleMappings.find( + ({ roleMapping }) => roleMapping.id === roleMappingId + ); + if (singleUserRoleMapping) { + actions.setElasticsearchUser(singleUserRoleMapping.elasticsearchUser); + actions.setRoleMapping(singleUserRoleMapping.roleMapping); + } + actions.setSingleUserRoleMapping(singleUserRoleMapping); + }, handleDeleteMapping: async ({ roleMappingId }) => { const { http } = HttpLogic.values; const route = `/api/app_search/role_mappings/${roleMappingId}`; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts index ae9d3da060fe1..66b917f169ca9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts @@ -15,11 +15,18 @@ import { groups } from '../../__mocks__/groups.mock'; import { nextTick } from '@kbn/test/jest'; -import { wsRoleMapping } from '../../../shared/role_mapping/__mocks__/roles'; +import { elasticsearchUsers } from '../../../shared/role_mapping/__mocks__/elasticsearchUsers'; + +import { + wsRoleMapping, + wsSingleUserRoleMapping, +} from '../../../shared/role_mapping/__mocks__/roles'; import { ANY_AUTH_PROVIDER } from '../../../shared/role_mapping/constants'; import { RoleMappingsLogic } from './role_mappings_logic'; +const emptyUser = { username: '', email: '' }; + describe('RoleMappingsLogic', () => { const { http } = mockHttpValues; const { clearFlashMessages, flashAPIErrors } = mockFlashMessageHelpers; @@ -28,6 +35,8 @@ describe('RoleMappingsLogic', () => { attributes: [], availableAuthProviders: [], elasticsearchRoles: [], + elasticsearchUser: emptyUser, + elasticsearchUsers: [], roleMapping: null, roleMappingFlyoutOpen: false, roleMappings: [], @@ -42,6 +51,8 @@ describe('RoleMappingsLogic', () => { selectedAuthProviders: [ANY_AUTH_PROVIDER], selectedOptions: [], roleMappingErrors: [], + singleUserRoleMapping: null, + singleUserRoleMappings: [], }; const roleGroup = { id: '123', @@ -59,6 +70,8 @@ describe('RoleMappingsLogic', () => { authProviders: [], availableGroups: [roleGroup, defaultGroup], elasticsearchRoles: [], + singleUserRoleMappings: [wsSingleUserRoleMapping], + elasticsearchUsers, }; beforeEach(() => { @@ -71,23 +84,36 @@ describe('RoleMappingsLogic', () => { }); describe('actions', () => { - it('setRoleMappingsData', () => { - RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + describe('setRoleMappingsData', () => { + it('sets data based on server response from the `mappings` (plural) endpoint', () => { + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); - expect(RoleMappingsLogic.values.roleMappings).toEqual([wsRoleMapping]); - expect(RoleMappingsLogic.values.dataLoading).toEqual(false); - expect(RoleMappingsLogic.values.multipleAuthProvidersConfig).toEqual(true); - expect(RoleMappingsLogic.values.dataLoading).toEqual(false); - expect(RoleMappingsLogic.values.attributes).toEqual(mappingsServerProps.attributes); - expect(RoleMappingsLogic.values.availableGroups).toEqual(mappingsServerProps.availableGroups); - expect(RoleMappingsLogic.values.includeInAllGroups).toEqual(false); - expect(RoleMappingsLogic.values.elasticsearchRoles).toEqual( - mappingsServerProps.elasticsearchRoles - ); - expect(RoleMappingsLogic.values.selectedOptions).toEqual([ - { label: defaultGroup.name, value: defaultGroup.id }, - ]); - expect(RoleMappingsLogic.values.selectedGroups).toEqual(new Set([defaultGroup.id])); + expect(RoleMappingsLogic.values.roleMappings).toEqual([wsRoleMapping]); + expect(RoleMappingsLogic.values.dataLoading).toEqual(false); + expect(RoleMappingsLogic.values.multipleAuthProvidersConfig).toEqual(true); + expect(RoleMappingsLogic.values.dataLoading).toEqual(false); + expect(RoleMappingsLogic.values.attributes).toEqual(mappingsServerProps.attributes); + expect(RoleMappingsLogic.values.availableGroups).toEqual( + mappingsServerProps.availableGroups + ); + expect(RoleMappingsLogic.values.includeInAllGroups).toEqual(false); + expect(RoleMappingsLogic.values.elasticsearchRoles).toEqual( + mappingsServerProps.elasticsearchRoles + ); + expect(RoleMappingsLogic.values.selectedOptions).toEqual([ + { label: defaultGroup.name, value: defaultGroup.id }, + ]); + expect(RoleMappingsLogic.values.selectedGroups).toEqual(new Set([defaultGroup.id])); + }); + + it('handles fallback if no elasticsearch users present', () => { + RoleMappingsLogic.actions.setRoleMappingsData({ + ...mappingsServerProps, + elasticsearchUsers: [], + }); + + expect(RoleMappingsLogic.values.elasticsearchUser).toEqual(emptyUser); + }); }); it('setRoleMappings', () => { @@ -97,6 +123,26 @@ describe('RoleMappingsLogic', () => { expect(RoleMappingsLogic.values.dataLoading).toEqual(false); }); + describe('setElasticsearchUser', () => { + it('sets user', () => { + RoleMappingsLogic.actions.setElasticsearchUser(elasticsearchUsers[0]); + + expect(RoleMappingsLogic.values.elasticsearchUser).toEqual(elasticsearchUsers[0]); + }); + + it('handles fallback if no user present', () => { + RoleMappingsLogic.actions.setElasticsearchUser(undefined); + + expect(RoleMappingsLogic.values.elasticsearchUser).toEqual(emptyUser); + }); + }); + + it('setSingleUserRoleMapping', () => { + RoleMappingsLogic.actions.setSingleUserRoleMapping(wsSingleUserRoleMapping); + + expect(RoleMappingsLogic.values.singleUserRoleMapping).toEqual(wsSingleUserRoleMapping); + }); + it('handleRoleChange', () => { RoleMappingsLogic.actions.handleRoleChange('user'); @@ -303,6 +349,39 @@ describe('RoleMappingsLogic', () => { }); }); + describe('initializeSingleUserRoleMapping', () => { + let setElasticsearchUserSpy: jest.MockedFunction; + let setRoleMappingSpy: jest.MockedFunction; + let setSingleUserRoleMappingSpy: jest.MockedFunction; + beforeEach(() => { + setElasticsearchUserSpy = jest.spyOn(RoleMappingsLogic.actions, 'setElasticsearchUser'); + setRoleMappingSpy = jest.spyOn(RoleMappingsLogic.actions, 'setRoleMapping'); + setSingleUserRoleMappingSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setSingleUserRoleMapping' + ); + }); + + it('should handle the new user state and only set an empty mapping', () => { + RoleMappingsLogic.actions.initializeSingleUserRoleMapping(); + + expect(setElasticsearchUserSpy).not.toHaveBeenCalled(); + expect(setRoleMappingSpy).not.toHaveBeenCalled(); + expect(setSingleUserRoleMappingSpy).toHaveBeenCalledWith(undefined); + }); + + it('should handle an existing user state and set mapping', () => { + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + RoleMappingsLogic.actions.initializeSingleUserRoleMapping( + wsSingleUserRoleMapping.roleMapping.id + ); + + expect(setElasticsearchUserSpy).toHaveBeenCalled(); + expect(setRoleMappingSpy).toHaveBeenCalled(); + expect(setSingleUserRoleMappingSpy).toHaveBeenCalledWith(wsSingleUserRoleMapping); + }); + }); + describe('handleSaveMapping', () => { it('calls API and refreshes list when new mapping', async () => { const initializeRoleMappingsSpy = jest.spyOn( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts index 4b4b38aff957d..a0ca516737d79 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts @@ -16,7 +16,7 @@ import { } from '../../../shared/flash_messages'; import { HttpLogic } from '../../../shared/http'; import { ANY_AUTH_PROVIDER } from '../../../shared/role_mapping/constants'; -import { AttributeName } from '../../../shared/types'; +import { AttributeName, SingleUserRoleMapping, ElasticsearchUser } from '../../../shared/types'; import { RoleGroup, WSRoleMapping, Role } from '../../types'; import { @@ -26,19 +26,24 @@ import { DEFAULT_GROUP_NAME, } from './constants'; +type UserMapping = SingleUserRoleMapping; + interface RoleMappingsServerDetails { roleMappings: WSRoleMapping[]; attributes: string[]; authProviders: string[]; availableGroups: RoleGroup[]; + elasticsearchUsers: ElasticsearchUser[]; elasticsearchRoles: string[]; multipleAuthProvidersConfig: boolean; + singleUserRoleMappings: UserMapping[]; } const getFirstAttributeName = (roleMapping: WSRoleMapping): AttributeName => Object.entries(roleMapping.rules)[0][0] as AttributeName; const getFirstAttributeValue = (roleMapping: WSRoleMapping): string => Object.entries(roleMapping.rules)[0][1] as string; +const emptyUser = { username: '', email: '' } as ElasticsearchUser; interface RoleMappingsActions { handleAllGroupsSelectionChange(selected: boolean): { selected: boolean }; @@ -53,15 +58,20 @@ interface RoleMappingsActions { handleRoleChange(roleType: Role): { roleType: Role }; handleSaveMapping(): void; initializeRoleMapping(roleMappingId?: string): { roleMappingId?: string }; + initializeSingleUserRoleMapping(roleMappingId?: string): { roleMappingId?: string }; initializeRoleMappings(): void; resetState(): void; setRoleMapping(roleMapping: WSRoleMapping): { roleMapping: WSRoleMapping }; + setSingleUserRoleMapping(data?: UserMapping): { singleUserRoleMapping: UserMapping }; setRoleMappings({ roleMappings, }: { roleMappings: WSRoleMapping[]; }): { roleMappings: WSRoleMapping[] }; setRoleMappingsData(data: RoleMappingsServerDetails): RoleMappingsServerDetails; + setElasticsearchUser( + elasticsearchUser?: ElasticsearchUser + ): { elasticsearchUser: ElasticsearchUser }; openRoleMappingFlyout(): void; closeUsersAndRolesFlyout(): void; setRoleMappingErrors(errors: string[]): { errors: string[] }; @@ -77,9 +87,13 @@ interface RoleMappingsValues { availableGroups: RoleGroup[]; dataLoading: boolean; elasticsearchRoles: string[]; + elasticsearchUsers: ElasticsearchUser[]; + elasticsearchUser: ElasticsearchUser; multipleAuthProvidersConfig: boolean; roleMapping: WSRoleMapping | null; roleMappings: WSRoleMapping[]; + singleUserRoleMapping: UserMapping | null; + singleUserRoleMappings: UserMapping[]; roleType: Role; selectedAuthProviders: string[]; selectedGroups: Set; @@ -93,6 +107,8 @@ export const RoleMappingsLogic = kea data, setRoleMapping: (roleMapping: WSRoleMapping) => ({ roleMapping }), + setElasticsearchUser: (elasticsearchUser: ElasticsearchUser) => ({ elasticsearchUser }), + setSingleUserRoleMapping: (singleUserRoleMapping: UserMapping) => ({ singleUserRoleMapping }), setRoleMappings: ({ roleMappings }: { roleMappings: WSRoleMapping[] }) => ({ roleMappings }), setRoleMappingErrors: (errors: string[]) => ({ errors }), handleAuthProviderChange: (value: string[]) => ({ value }), @@ -107,6 +123,7 @@ export const RoleMappingsLogic = kea ({ roleMappingId }), initializeRoleMapping: (roleMappingId?: string) => ({ roleMappingId }), handleDeleteMapping: (roleMappingId: string) => ({ roleMappingId }), handleSaveMapping: true, @@ -131,6 +148,13 @@ export const RoleMappingsLogic = kea [], }, ], + singleUserRoleMappings: [ + [], + { + setRoleMappingsData: (_, { singleUserRoleMappings }) => singleUserRoleMappings, + resetState: () => [], + }, + ], multipleAuthProvidersConfig: [ false, { @@ -156,6 +180,12 @@ export const RoleMappingsLogic = kea elasticsearchRoles, }, ], + elasticsearchUsers: [ + [], + { + setRoleMappingsData: (_, { elasticsearchUsers }) => elasticsearchUsers, + }, + ], roleMapping: [ null, { @@ -164,6 +194,13 @@ export const RoleMappingsLogic = kea null, }, ], + singleUserRoleMapping: [ + null, + { + setSingleUserRoleMapping: (_, { singleUserRoleMapping }) => singleUserRoleMapping || null, + closeUsersAndRolesFlyout: () => null, + }, + ], roleType: [ 'admin', { @@ -257,6 +294,14 @@ export const RoleMappingsLogic = kea [], }, ], + elasticsearchUser: [ + emptyUser, + { + setRoleMappingsData: (_, { elasticsearchUsers }) => elasticsearchUsers[0] || emptyUser, + setElasticsearchUser: (_, { elasticsearchUser }) => elasticsearchUser || emptyUser, + closeUsersAndRolesFlyout: () => emptyUser, + }, + ], }, selectors: ({ selectors }) => ({ selectedOptions: [ @@ -296,6 +341,17 @@ export const RoleMappingsLogic = kea id === roleMappingId); if (roleMapping) actions.setRoleMapping(roleMapping); }, + initializeSingleUserRoleMapping: ({ roleMappingId }) => { + const singleUserRoleMapping = values.singleUserRoleMappings.find( + ({ roleMapping }) => roleMapping.id === roleMappingId + ); + + if (singleUserRoleMapping) { + actions.setElasticsearchUser(singleUserRoleMapping.elasticsearchUser); + actions.setRoleMapping(singleUserRoleMapping.roleMapping); + } + actions.setSingleUserRoleMapping(singleUserRoleMapping); + }, handleDeleteMapping: async ({ roleMappingId }) => { const { http } = HttpLogic.values; const route = `/api/workplace_search/org/role_mappings/${roleMappingId}`; From 1881ef5dc9e142dff24e9004c0e752d42cbbf618 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Tue, 22 Jun 2021 14:22:30 -0500 Subject: [PATCH 03/21] Add logic for various form states - Showing and hiding flyouts - Select and text input values - User created state to turn flyout into a success message state --- .../role_mappings/role_mappings_logic.test.ts | 100 ++++++++++++++++++ .../role_mappings/role_mappings_logic.ts | 71 +++++++++++++ .../role_mappings/role_mappings_logic.test.ts | 91 ++++++++++++++++ .../role_mappings/role_mappings_logic.ts | 71 +++++++++++++ 4 files changed, 333 insertions(+) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts index c3261fcc39aec..6eb543e529fea 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts @@ -54,6 +54,10 @@ describe('RoleMappingsLogic', () => { roleMappingErrors: [], singleUserRoleMapping: null, singleUserRoleMappings: [], + singleUserRoleMappingFlyoutOpen: false, + userCreated: false, + userFormIsNewUser: true, + userFormUserIsExisting: true, }; const mappingsServerProps = { @@ -197,6 +201,12 @@ describe('RoleMappingsLogic', () => { }); }); + it('setUserExistingRadioValue', () => { + RoleMappingsLogic.actions.setUserExistingRadioValue(false); + + expect(RoleMappingsLogic.values.userFormUserIsExisting).toEqual(false); + }); + describe('handleAttributeSelectorChange', () => { const elasticsearchRoles = ['foo', 'bar']; @@ -307,6 +317,14 @@ describe('RoleMappingsLogic', () => { expect(clearFlashMessages).toHaveBeenCalled(); }); + it('openSingleUserRoleMappingFlyout', () => { + mount(mappingsServerProps); + RoleMappingsLogic.actions.openSingleUserRoleMappingFlyout(); + + expect(RoleMappingsLogic.values.singleUserRoleMappingFlyoutOpen).toEqual(true); + expect(clearFlashMessages).toHaveBeenCalled(); + }); + it('closeUsersAndRolesFlyout', () => { mount({ ...mappingsServerProps, @@ -317,6 +335,41 @@ describe('RoleMappingsLogic', () => { expect(RoleMappingsLogic.values.roleMappingFlyoutOpen).toEqual(false); expect(clearFlashMessages).toHaveBeenCalled(); }); + + it('setElasticsearchUsernameValue', () => { + const username = 'newName'; + RoleMappingsLogic.actions.setElasticsearchUsernameValue(username); + + expect(RoleMappingsLogic.values).toEqual({ + ...DEFAULT_VALUES, + elasticsearchUser: { + ...RoleMappingsLogic.values.elasticsearchUser, + username, + }, + }); + }); + + it('setElasticsearchEmailValue', () => { + const email = 'newEmail@foo.cats'; + RoleMappingsLogic.actions.setElasticsearchEmailValue(email); + + expect(RoleMappingsLogic.values).toEqual({ + ...DEFAULT_VALUES, + elasticsearchUser: { + ...RoleMappingsLogic.values.elasticsearchUser, + email, + }, + }); + }); + + it('setUserCreated', () => { + RoleMappingsLogic.actions.setUserCreated(); + + expect(RoleMappingsLogic.values).toEqual({ + ...DEFAULT_VALUES, + userCreated: true, + }); + }); }); describe('listeners', () => { @@ -538,5 +591,52 @@ describe('RoleMappingsLogic', () => { expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); + + describe('handleUsernameSelectChange', () => { + it('sets elasticsearchUser when match found', () => { + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + const setElasticsearchUserSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setElasticsearchUser' + ); + RoleMappingsLogic.actions.handleUsernameSelectChange(elasticsearchUsers[0].username); + + expect(setElasticsearchUserSpy).toHaveBeenCalledWith(elasticsearchUsers[0]); + }); + + it('does not set elasticsearchUser when no match found', () => { + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + const setElasticsearchUserSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setElasticsearchUser' + ); + RoleMappingsLogic.actions.handleUsernameSelectChange('bogus'); + + expect(setElasticsearchUserSpy).not.toHaveBeenCalled(); + }); + }); + + describe('setUserExistingRadioValue', () => { + it('handles existing user', () => { + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + const setElasticsearchUserSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setElasticsearchUser' + ); + RoleMappingsLogic.actions.setUserExistingRadioValue(true); + + expect(setElasticsearchUserSpy).toHaveBeenCalledWith(elasticsearchUsers[0]); + }); + + it('handles new user', () => { + const setElasticsearchUserSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setElasticsearchUser' + ); + RoleMappingsLogic.actions.setUserExistingRadioValue(false); + + expect(setElasticsearchUserSpy).toHaveBeenCalledWith(emptyUser); + }); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts index 65ceb0051ec21..28fdf64da5e02 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts @@ -58,6 +58,7 @@ interface RoleMappingsActions { handleDeleteMapping(roleMappingId: string): { roleMappingId: string }; handleEngineSelectionChange(engineNames: string[]): { engineNames: string[] }; handleRoleChange(roleType: RoleTypes): { roleType: RoleTypes }; + handleUsernameSelectChange(username: string): { username: string }; handleSaveMapping(): void; initializeRoleMapping(roleMappingId?: string): { roleMappingId?: string }; initializeSingleUserRoleMapping(roleMappingId?: string): { roleMappingId?: string }; @@ -75,9 +76,15 @@ interface RoleMappingsActions { elasticsearchUser?: ElasticsearchUser ): { elasticsearchUser: ElasticsearchUser }; openRoleMappingFlyout(): void; + openSingleUserRoleMappingFlyout(): void; closeUsersAndRolesFlyout(): void; setRoleMappingErrors(errors: string[]): { errors: string[] }; enableRoleBasedAccess(): void; + setUserExistingRadioValue(userFormUserIsExisting: boolean): { userFormUserIsExisting: boolean }; + setElasticsearchUsernameValue(username: string): { username: string }; + setElasticsearchEmailValue(email: string): { email: string }; + setUserCreated(): void; + setUserFormIsNewUser(userFormIsNewUser: boolean): { userFormIsNewUser: boolean }; } interface RoleMappingsValues { @@ -101,8 +108,12 @@ interface RoleMappingsValues { selectedAuthProviders: string[]; selectedEngines: Set; roleMappingFlyoutOpen: boolean; + singleUserRoleMappingFlyoutOpen: boolean; selectedOptions: EuiComboBoxOptionOption[]; roleMappingErrors: string[]; + userFormUserIsExisting: boolean; + userCreated: boolean; + userFormIsNewUser: boolean; } export const RoleMappingsLogic = kea>({ @@ -116,6 +127,7 @@ export const RoleMappingsLogic = kea ({ errors }), handleAuthProviderChange: (value: string) => ({ value }), handleRoleChange: (roleType: RoleTypes) => ({ roleType }), + handleUsernameSelectChange: (username: string) => ({ username }), handleEngineSelectionChange: (engineNames: string[]) => ({ engineNames }), handleAttributeSelectorChange: (value: string, firstElasticsearchRole: string) => ({ value, @@ -124,6 +136,8 @@ export const RoleMappingsLogic = kea ({ value }), handleAccessAllEnginesChange: (selected: boolean) => ({ selected }), enableRoleBasedAccess: true, + openSingleUserRoleMappingFlyout: true, + setUserExistingRadioValue: (userFormUserIsExisting: boolean) => ({ userFormUserIsExisting }), resetState: true, initializeRoleMappings: true, initializeSingleUserRoleMapping: (roleMappingId?: string) => ({ roleMappingId }), @@ -132,6 +146,10 @@ export const RoleMappingsLogic = kea ({ username }), + setElasticsearchEmailValue: (email: string) => ({ email }), + setUserCreated: true, + setUserFormIsNewUser: (userFormIsNewUser: boolean) => ({ userFormIsNewUser }), }, reducers: { dataLoading: [ @@ -189,6 +207,7 @@ export const RoleMappingsLogic = kea elasticsearchRoles, + closeUsersAndRolesFlyout: () => [ANY_AUTH_PROVIDER], }, ], elasticsearchUsers: [ @@ -219,6 +238,7 @@ export const RoleMappingsLogic = kea roleMapping.accessAllEngines, handleRoleChange: (_, { roleType }) => !roleHasScopedEngines(roleType), handleAccessAllEnginesChange: (_, { selected }) => selected, + closeUsersAndRolesFlyout: () => true, }, ], attributeValue: [ @@ -253,6 +273,7 @@ export const RoleMappingsLogic = kea new Set(), }, ], availableAuthProviders: [ @@ -287,6 +308,14 @@ export const RoleMappingsLogic = kea true, }, ], + singleUserRoleMappingFlyoutOpen: [ + false, + { + openSingleUserRoleMappingFlyout: () => true, + closeUsersAndRolesFlyout: () => false, + initializeSingleUserRoleMapping: () => true, + }, + ], singleUserRoleMapping: [ null, { @@ -302,14 +331,42 @@ export const RoleMappingsLogic = kea [], }, ], + userFormUserIsExisting: [ + true, + { + setUserExistingRadioValue: (_, { userFormUserIsExisting }) => userFormUserIsExisting, + closeUsersAndRolesFlyout: () => true, + }, + ], elasticsearchUser: [ emptyUser, { setRoleMappingsData: (_, { elasticsearchUsers }) => elasticsearchUsers[0] || emptyUser, setElasticsearchUser: (_, { elasticsearchUser }) => elasticsearchUser || emptyUser, + setElasticsearchUsernameValue: (state, { username }) => ({ + ...state, + username, + }), + setElasticsearchEmailValue: (state, { email }) => ({ + ...state, + email, + }), closeUsersAndRolesFlyout: () => emptyUser, }, ], + userCreated: [ + false, + { + setUserCreated: () => true, + closeUsersAndRolesFlyout: () => false, + }, + ], + userFormIsNewUser: [ + true, + { + setUserFormIsNewUser: (_, { userFormIsNewUser }) => userFormIsNewUser, + }, + ], }, selectors: ({ selectors }) => ({ selectedOptions: [ @@ -358,6 +415,7 @@ export const RoleMappingsLogic = kea { const { http } = HttpLogic.values; @@ -415,9 +473,22 @@ export const RoleMappingsLogic = kea { clearFlashMessages(); + const firstUser = values.elasticsearchUsers[0]; + actions.setElasticsearchUser(firstUser); }, openRoleMappingFlyout: () => { clearFlashMessages(); }, + openSingleUserRoleMappingFlyout: () => { + clearFlashMessages(); + }, + setUserExistingRadioValue: ({ userFormUserIsExisting }) => { + const firstUser = values.elasticsearchUsers[0]; + actions.setElasticsearchUser(userFormUserIsExisting ? firstUser : emptyUser); + }, + handleUsernameSelectChange: ({ username }) => { + const user = values.elasticsearchUsers.find((u) => u.username === username); + if (user) actions.setElasticsearchUser(user); + }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts index 66b917f169ca9..6fd2e10de3261 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts @@ -53,6 +53,10 @@ describe('RoleMappingsLogic', () => { roleMappingErrors: [], singleUserRoleMapping: null, singleUserRoleMappings: [], + singleUserRoleMappingFlyoutOpen: false, + userCreated: false, + userFormIsNewUser: true, + userFormUserIsExisting: true, }; const roleGroup = { id: '123', @@ -179,6 +183,12 @@ describe('RoleMappingsLogic', () => { expect(RoleMappingsLogic.values.includeInAllGroups).toEqual(true); }); + it('setUserExistingRadioValue', () => { + RoleMappingsLogic.actions.setUserExistingRadioValue(false); + + expect(RoleMappingsLogic.values.userFormUserIsExisting).toEqual(false); + }); + describe('handleAttributeSelectorChange', () => { const elasticsearchRoles = ['foo', 'bar']; @@ -274,6 +284,14 @@ describe('RoleMappingsLogic', () => { expect(clearFlashMessages).toHaveBeenCalled(); }); + it('openSingleUserRoleMappingFlyout', () => { + mount(mappingsServerProps); + RoleMappingsLogic.actions.openSingleUserRoleMappingFlyout(); + + expect(RoleMappingsLogic.values.singleUserRoleMappingFlyoutOpen).toEqual(true); + expect(clearFlashMessages).toHaveBeenCalled(); + }); + it('closeUsersAndRolesFlyout', () => { mount({ ...mappingsServerProps, @@ -284,6 +302,32 @@ describe('RoleMappingsLogic', () => { expect(RoleMappingsLogic.values.roleMappingFlyoutOpen).toEqual(false); expect(clearFlashMessages).toHaveBeenCalled(); }); + + it('setElasticsearchUsernameValue', () => { + const username = 'newName'; + RoleMappingsLogic.actions.setElasticsearchUsernameValue(username); + + expect(RoleMappingsLogic.values.elasticsearchUser).toEqual({ + ...RoleMappingsLogic.values.elasticsearchUser, + username, + }); + }); + + it('setElasticsearchEmailValue', () => { + const email = 'newEmail@foo.cats'; + RoleMappingsLogic.actions.setElasticsearchEmailValue(email); + + expect(RoleMappingsLogic.values.elasticsearchUser).toEqual({ + ...RoleMappingsLogic.values.elasticsearchUser, + email, + }); + }); + + it('setUserCreated', () => { + RoleMappingsLogic.actions.setUserCreated(); + + expect(RoleMappingsLogic.values.userCreated).toEqual(true); + }); }); describe('listeners', () => { @@ -489,5 +533,52 @@ describe('RoleMappingsLogic', () => { expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); }); + + describe('handleUsernameSelectChange', () => { + it('sets elasticsearchUser when match found', () => { + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + const setElasticsearchUserSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setElasticsearchUser' + ); + RoleMappingsLogic.actions.handleUsernameSelectChange(elasticsearchUsers[0].username); + + expect(setElasticsearchUserSpy).toHaveBeenCalledWith(elasticsearchUsers[0]); + }); + + it('does not set elasticsearchUser when no match found', () => { + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + const setElasticsearchUserSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setElasticsearchUser' + ); + RoleMappingsLogic.actions.handleUsernameSelectChange('bogus'); + + expect(setElasticsearchUserSpy).not.toHaveBeenCalled(); + }); + }); + + describe('setUserExistingRadioValue', () => { + it('handles existing user', () => { + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + const setElasticsearchUserSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setElasticsearchUser' + ); + RoleMappingsLogic.actions.setUserExistingRadioValue(true); + + expect(setElasticsearchUserSpy).toHaveBeenCalledWith(elasticsearchUsers[0]); + }); + + it('handles new user', () => { + const setElasticsearchUserSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setElasticsearchUser' + ); + RoleMappingsLogic.actions.setUserExistingRadioValue(false); + + expect(setElasticsearchUserSpy).toHaveBeenCalledWith(emptyUser); + }); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts index a0ca516737d79..f44db939f4340 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts @@ -56,6 +56,7 @@ interface RoleMappingsActions { handleDeleteMapping(roleMappingId: string): { roleMappingId: string }; handleGroupSelectionChange(groupIds: string[]): { groupIds: string[] }; handleRoleChange(roleType: Role): { roleType: Role }; + handleUsernameSelectChange(username: string): { username: string }; handleSaveMapping(): void; initializeRoleMapping(roleMappingId?: string): { roleMappingId?: string }; initializeSingleUserRoleMapping(roleMappingId?: string): { roleMappingId?: string }; @@ -73,9 +74,15 @@ interface RoleMappingsActions { elasticsearchUser?: ElasticsearchUser ): { elasticsearchUser: ElasticsearchUser }; openRoleMappingFlyout(): void; + openSingleUserRoleMappingFlyout(): void; closeUsersAndRolesFlyout(): void; setRoleMappingErrors(errors: string[]): { errors: string[] }; enableRoleBasedAccess(): void; + setUserExistingRadioValue(userFormUserIsExisting: boolean): { userFormUserIsExisting: boolean }; + setElasticsearchUsernameValue(username: string): { username: string }; + setElasticsearchEmailValue(email: string): { email: string }; + setUserCreated(): void; + setUserFormIsNewUser(userFormIsNewUser: boolean): { userFormIsNewUser: boolean }; } interface RoleMappingsValues { @@ -98,8 +105,12 @@ interface RoleMappingsValues { selectedAuthProviders: string[]; selectedGroups: Set; roleMappingFlyoutOpen: boolean; + singleUserRoleMappingFlyoutOpen: boolean; selectedOptions: EuiComboBoxOptionOption[]; roleMappingErrors: string[]; + userFormUserIsExisting: boolean; + userCreated: boolean; + userFormIsNewUser: boolean; } export const RoleMappingsLogic = kea>({ @@ -113,6 +124,7 @@ export const RoleMappingsLogic = kea ({ errors }), handleAuthProviderChange: (value: string[]) => ({ value }), handleRoleChange: (roleType: Role) => ({ roleType }), + handleUsernameSelectChange: (username: string) => ({ username }), handleGroupSelectionChange: (groupIds: string[]) => ({ groupIds }), handleAttributeSelectorChange: (value: string, firstElasticsearchRole: string) => ({ value, @@ -121,6 +133,8 @@ export const RoleMappingsLogic = kea ({ value }), handleAllGroupsSelectionChange: (selected: boolean) => ({ selected }), enableRoleBasedAccess: true, + openSingleUserRoleMappingFlyout: true, + setUserExistingRadioValue: (userFormUserIsExisting: boolean) => ({ userFormUserIsExisting }), resetState: true, initializeRoleMappings: true, initializeSingleUserRoleMapping: (roleMappingId?: string) => ({ roleMappingId }), @@ -129,6 +143,10 @@ export const RoleMappingsLogic = kea ({ username }), + setElasticsearchEmailValue: (email: string) => ({ email }), + setUserCreated: true, + setUserFormIsNewUser: (userFormIsNewUser: boolean) => ({ userFormIsNewUser }), }, reducers: { dataLoading: [ @@ -178,6 +196,7 @@ export const RoleMappingsLogic = kea elasticsearchRoles, + closeUsersAndRolesFlyout: () => [ANY_AUTH_PROVIDER], }, ], elasticsearchUsers: [ @@ -213,6 +232,7 @@ export const RoleMappingsLogic = kea roleMapping.allGroups, handleAllGroupsSelectionChange: (_, { selected }) => selected, + closeUsersAndRolesFlyout: () => false, }, ], attributeValue: [ @@ -252,6 +272,7 @@ export const RoleMappingsLogic = kea new Set(), }, ], availableAuthProviders: [ @@ -286,6 +307,14 @@ export const RoleMappingsLogic = kea true, }, ], + singleUserRoleMappingFlyoutOpen: [ + false, + { + openSingleUserRoleMappingFlyout: () => true, + closeUsersAndRolesFlyout: () => false, + initializeSingleUserRoleMapping: () => true, + }, + ], roleMappingErrors: [ [], { @@ -294,14 +323,42 @@ export const RoleMappingsLogic = kea [], }, ], + userFormUserIsExisting: [ + true, + { + setUserExistingRadioValue: (_, { userFormUserIsExisting }) => userFormUserIsExisting, + closeUsersAndRolesFlyout: () => true, + }, + ], elasticsearchUser: [ emptyUser, { setRoleMappingsData: (_, { elasticsearchUsers }) => elasticsearchUsers[0] || emptyUser, setElasticsearchUser: (_, { elasticsearchUser }) => elasticsearchUser || emptyUser, + setElasticsearchUsernameValue: (state, { username }) => ({ + ...state, + username, + }), + setElasticsearchEmailValue: (state, { email }) => ({ + ...state, + email, + }), closeUsersAndRolesFlyout: () => emptyUser, }, ], + userCreated: [ + false, + { + setUserCreated: () => true, + closeUsersAndRolesFlyout: () => false, + }, + ], + userFormIsNewUser: [ + true, + { + setUserFormIsNewUser: (_, { userFormIsNewUser }) => userFormIsNewUser, + }, + ], }, selectors: ({ selectors }) => ({ selectedOptions: [ @@ -351,6 +408,7 @@ export const RoleMappingsLogic = kea { const { http } = HttpLogic.values; @@ -407,9 +465,22 @@ export const RoleMappingsLogic = kea { clearFlashMessages(); + const firstUser = values.elasticsearchUsers[0]; + actions.setElasticsearchUser(firstUser); }, openRoleMappingFlyout: () => { clearFlashMessages(); }, + openSingleUserRoleMappingFlyout: () => { + clearFlashMessages(); + }, + setUserExistingRadioValue: ({ userFormUserIsExisting }) => { + const firstUser = values.elasticsearchUsers[0]; + actions.setElasticsearchUser(userFormUserIsExisting ? firstUser : emptyUser); + }, + handleUsernameSelectChange: ({ username }) => { + const user = values.elasticsearchUsers.find((u) => u.username === username); + if (user) actions.setElasticsearchUser(user); + }, }), }); From c154afc325679ac942fa88eb22841dfa81faba3e Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Tue, 22 Jun 2021 15:36:07 -0500 Subject: [PATCH 04/21] Add User server routes --- .../routes/app_search/role_mappings.test.ts | 24 +++++++++++++++ .../server/routes/app_search/role_mappings.ts | 26 +++++++++++++++++ .../workplace_search/role_mappings.test.ts | 24 +++++++++++++++ .../routes/workplace_search/role_mappings.ts | 29 +++++++++++++++++++ 4 files changed, 103 insertions(+) diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts index 7d9f08627516b..fe9c1c80dae4c 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts @@ -11,6 +11,7 @@ import { registerEnableRoleMappingsRoute, registerRoleMappingsRoute, registerRoleMappingRoute, + registerUserRoute, } from './role_mappings'; const roleMappingBaseSchema = { @@ -160,4 +161,27 @@ describe('role mappings routes', () => { }); }); }); + + describe('POST /api/app_search/single_user_role_mapping', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'post', + path: '/api/app_search/single_user_role_mapping', + }); + + registerUserRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/as/role_mappings/upsert_single_user_role_mapping', + }); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts index da620be2ea950..d90a005cb2532 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.ts @@ -93,8 +93,34 @@ export function registerRoleMappingRoute({ ); } +export function registerUserRoute({ router, enterpriseSearchRequestHandler }: RouteDependencies) { + router.post( + { + path: '/api/app_search/single_user_role_mapping', + validate: { + body: schema.object({ + roleMapping: schema.object({ + engines: schema.arrayOf(schema.string()), + roleType: schema.string(), + accessAllEngines: schema.boolean(), + id: schema.maybe(schema.string()), + }), + elasticsearchUser: schema.object({ + username: schema.string(), + email: schema.string(), + }), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/as/role_mappings/upsert_single_user_role_mapping', + }) + ); +} + export const registerRoleMappingsRoutes = (dependencies: RouteDependencies) => { registerEnableRoleMappingsRoute(dependencies); registerRoleMappingsRoute(dependencies); registerRoleMappingRoute(dependencies); + registerUserRoute(dependencies); }; diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts index aa0e9983166c0..abaf1d6439ef1 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts @@ -11,6 +11,7 @@ import { registerOrgEnableRoleMappingsRoute, registerOrgRoleMappingsRoute, registerOrgRoleMappingRoute, + registerOrgUserRoute, } from './role_mappings'; describe('role mappings routes', () => { @@ -128,4 +129,27 @@ describe('role mappings routes', () => { }); }); }); + + describe('POST /api/workplace_search/org/single_user_role_mapping', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'post', + path: '/api/workplace_search/org/single_user_role_mapping', + }); + + registerOrgUserRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/ws/org/role_mappings/upsert_single_user_role_mapping', + }); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.ts index cea7bcb311ce8..e6f4919ed2a2f 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.ts @@ -93,8 +93,37 @@ export function registerOrgRoleMappingRoute({ ); } +export function registerOrgUserRoute({ + router, + enterpriseSearchRequestHandler, +}: RouteDependencies) { + router.post( + { + path: '/api/workplace_search/org/single_user_role_mapping', + validate: { + body: schema.object({ + roleMapping: schema.object({ + groups: schema.arrayOf(schema.string()), + roleType: schema.string(), + allGroups: schema.boolean(), + id: schema.maybe(schema.string()), + }), + elasticsearchUser: schema.object({ + username: schema.string(), + email: schema.string(), + }), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/role_mappings/upsert_single_user_role_mapping', + }) + ); +} + export const registerRoleMappingsRoutes = (dependencies: RouteDependencies) => { registerOrgEnableRoleMappingsRoute(dependencies); registerOrgRoleMappingsRoute(dependencies); registerOrgRoleMappingRoute(dependencies); + registerOrgUserRoute(dependencies); }; From 859f9d1a26a1ff0cc3f36325d65b09b2dbda579e Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Tue, 22 Jun 2021 15:49:29 -0500 Subject: [PATCH 05/21] Add logic for saving a user --- .../role_mappings/role_mappings_logic.test.ts | 88 +++++++++++++++++ .../role_mappings/role_mappings_logic.ts | 34 +++++++ .../role_mappings/role_mappings_logic.test.ts | 94 +++++++++++++++++++ .../role_mappings/role_mappings_logic.ts | 36 +++++++ 4 files changed, 252 insertions(+) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts index 6eb543e529fea..311abadf6b579 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts @@ -563,6 +563,94 @@ describe('RoleMappingsLogic', () => { }); }); + describe('handleSaveUser', () => { + it('calls API and refreshes list when new mapping', async () => { + const initializeRoleMappingsSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'initializeRoleMappings' + ); + const setUserCreatedSpy = jest.spyOn(RoleMappingsLogic.actions, 'setUserCreated'); + const setSingleUserRoleMappingSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setSingleUserRoleMapping' + ); + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + + http.post.mockReturnValue(Promise.resolve(mappingsServerProps)); + RoleMappingsLogic.actions.handleSaveUser(); + + expect(http.post).toHaveBeenCalledWith('/api/app_search/single_user_role_mapping', { + body: JSON.stringify({ + roleMapping: { + engines: [], + roleType: 'owner', + accessAllEngines: true, + }, + elasticsearchUser: { + username: elasticsearchUsers[0].username, + email: elasticsearchUsers[0].email, + }, + }), + }); + await nextTick(); + + expect(initializeRoleMappingsSpy).toHaveBeenCalled(); + expect(setUserCreatedSpy).toHaveBeenCalled(); + expect(setSingleUserRoleMappingSpy).toHaveBeenCalled(); + }); + + it('calls API and refreshes list when existing mapping', async () => { + const initializeRoleMappingsSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'initializeRoleMappings' + ); + RoleMappingsLogic.actions.setSingleUserRoleMapping(asSingleUserRoleMapping); + RoleMappingsLogic.actions.handleAccessAllEnginesChange(false); + + http.put.mockReturnValue(Promise.resolve(mappingsServerProps)); + RoleMappingsLogic.actions.handleSaveUser(); + + expect(http.post).toHaveBeenCalledWith('/api/app_search/single_user_role_mapping', { + body: JSON.stringify({ + roleMapping: { + engines: [], + roleType: 'owner', + accessAllEngines: false, + id: asSingleUserRoleMapping.roleMapping.id, + }, + elasticsearchUser: { + username: '', + email: '', + }, + }), + }); + await nextTick(); + + expect(initializeRoleMappingsSpy).toHaveBeenCalled(); + }); + + it('handles error', async () => { + const setRoleMappingErrorsSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setRoleMappingErrors' + ); + + http.post.mockReturnValue( + Promise.reject({ + body: { + attributes: { + errors: ['this is an error'], + }, + }, + }) + ); + RoleMappingsLogic.actions.handleSaveUser(); + await nextTick(); + + expect(setRoleMappingErrorsSpy).toHaveBeenCalledWith(['this is an error']); + }); + }); + describe('handleDeleteMapping', () => { const roleMappingId = 'r1'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts index 28fdf64da5e02..c65692251c5ca 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts @@ -60,6 +60,7 @@ interface RoleMappingsActions { handleRoleChange(roleType: RoleTypes): { roleType: RoleTypes }; handleUsernameSelectChange(username: string): { username: string }; handleSaveMapping(): void; + handleSaveUser(): void; initializeRoleMapping(roleMappingId?: string): { roleMappingId?: string }; initializeSingleUserRoleMapping(roleMappingId?: string): { roleMappingId?: string }; initializeRoleMappings(): void; @@ -144,6 +145,7 @@ export const RoleMappingsLogic = kea ({ roleMappingId }), handleDeleteMapping: (roleMappingId: string) => ({ roleMappingId }), handleSaveMapping: true, + handleSaveUser: true, openRoleMappingFlyout: true, closeUsersAndRolesFlyout: false, setElasticsearchUsernameValue: (username: string) => ({ username }), @@ -471,6 +473,38 @@ export const RoleMappingsLogic = kea { clearFlashMessages(); }, + handleSaveUser: async () => { + const { http } = HttpLogic.values; + const { + roleType, + singleUserRoleMapping, + accessAllEngines, + selectedEngines, + elasticsearchUser: { email, username }, + } = values; + + const body = JSON.stringify({ + roleMapping: { + engines: accessAllEngines ? [] : Array.from(selectedEngines), + roleType, + accessAllEngines, + id: singleUserRoleMapping?.roleMapping?.id, + }, + elasticsearchUser: { + username, + email, + }, + }); + + try { + const response = await http.post('/api/app_search/single_user_role_mapping', { body }); + actions.setSingleUserRoleMapping(response); + actions.setUserCreated(); + actions.initializeRoleMappings(); + } catch (e) { + actions.setRoleMappingErrors(e?.body?.attributes?.errors); + } + }, closeUsersAndRolesFlyout: () => { clearFlashMessages(); const firstUser = values.elasticsearchUsers[0]; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts index 6fd2e10de3261..ac0536a4988c5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts @@ -504,6 +504,100 @@ describe('RoleMappingsLogic', () => { }); }); + describe('handleSaveUser', () => { + it('calls API and refreshes list when new mapping', async () => { + const initializeRoleMappingsSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'initializeRoleMappings' + ); + const setUserCreatedSpy = jest.spyOn(RoleMappingsLogic.actions, 'setUserCreated'); + const setSingleUserRoleMappingSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setSingleUserRoleMapping' + ); + RoleMappingsLogic.actions.setRoleMappingsData(mappingsServerProps); + + http.post.mockReturnValue(Promise.resolve(mappingsServerProps)); + RoleMappingsLogic.actions.handleSaveUser(); + + expect(http.post).toHaveBeenCalledWith( + '/api/workplace_search/org/single_user_role_mapping', + { + body: JSON.stringify({ + roleMapping: { + groups: [defaultGroup.id], + roleType: 'admin', + allGroups: false, + }, + elasticsearchUser: { + username: elasticsearchUsers[0].username, + email: elasticsearchUsers[0].email, + }, + }), + } + ); + await nextTick(); + + expect(initializeRoleMappingsSpy).toHaveBeenCalled(); + expect(setUserCreatedSpy).toHaveBeenCalled(); + expect(setSingleUserRoleMappingSpy).toHaveBeenCalled(); + }); + + it('calls API and refreshes list when existing mapping', async () => { + const initializeRoleMappingsSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'initializeRoleMappings' + ); + RoleMappingsLogic.actions.setSingleUserRoleMapping(wsSingleUserRoleMapping); + RoleMappingsLogic.actions.handleAllGroupsSelectionChange(true); + + http.put.mockReturnValue(Promise.resolve(mappingsServerProps)); + RoleMappingsLogic.actions.handleSaveUser(); + + expect(http.post).toHaveBeenCalledWith( + '/api/workplace_search/org/single_user_role_mapping', + { + body: JSON.stringify({ + roleMapping: { + groups: [], + roleType: 'admin', + allGroups: true, + id: wsSingleUserRoleMapping.roleMapping.id, + }, + elasticsearchUser: { + username: '', + email: '', + }, + }), + } + ); + await nextTick(); + + expect(initializeRoleMappingsSpy).toHaveBeenCalled(); + }); + + it('handles error', async () => { + const setRoleMappingErrorsSpy = jest.spyOn( + RoleMappingsLogic.actions, + 'setRoleMappingErrors' + ); + + http.post.mockReturnValue( + Promise.reject({ + body: { + attributes: { + errors: ['this is an error'], + }, + }, + }) + ); + RoleMappingsLogic.actions.handleSaveUser(); + await nextTick(); + + expect(setRoleMappingErrorsSpy).toHaveBeenCalledWith(['this is an error']); + }); + }); + describe('handleDeleteMapping', () => { const roleMappingId = 'r1'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts index f44db939f4340..add71c85c252b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts @@ -58,6 +58,7 @@ interface RoleMappingsActions { handleRoleChange(roleType: Role): { roleType: Role }; handleUsernameSelectChange(username: string): { username: string }; handleSaveMapping(): void; + handleSaveUser(): void; initializeRoleMapping(roleMappingId?: string): { roleMappingId?: string }; initializeSingleUserRoleMapping(roleMappingId?: string): { roleMappingId?: string }; initializeRoleMappings(): void; @@ -141,6 +142,7 @@ export const RoleMappingsLogic = kea ({ roleMappingId }), handleDeleteMapping: (roleMappingId: string) => ({ roleMappingId }), handleSaveMapping: true, + handleSaveUser: true, openRoleMappingFlyout: true, closeUsersAndRolesFlyout: false, setElasticsearchUsernameValue: (username: string) => ({ username }), @@ -463,6 +465,40 @@ export const RoleMappingsLogic = kea { clearFlashMessages(); }, + handleSaveUser: async () => { + const { http } = HttpLogic.values; + const { + roleType, + singleUserRoleMapping, + includeInAllGroups, + selectedGroups, + elasticsearchUser: { email, username }, + } = values; + + const body = JSON.stringify({ + roleMapping: { + groups: includeInAllGroups ? [] : Array.from(selectedGroups), + roleType, + allGroups: includeInAllGroups, + id: singleUserRoleMapping?.roleMapping?.id, + }, + elasticsearchUser: { + username, + email, + }, + }); + + try { + const response = await http.post('/api/workplace_search/org/single_user_role_mapping', { + body, + }); + actions.setSingleUserRoleMapping(response); + actions.setUserCreated(); + actions.initializeRoleMappings(); + } catch (e) { + actions.setRoleMappingErrors(e?.body?.attributes?.errors); + } + }, closeUsersAndRolesFlyout: () => { clearFlashMessages(); const firstUser = values.elasticsearchUsers[0]; From ccf902c89fd366532a91cb0a7f7a89f013ffaf92 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 13:14:41 -0500 Subject: [PATCH 06/21] Add User components --- .../components/role_mappings/user.test.tsx | 124 ++++++++++++++++++ .../components/role_mappings/user.tsx | 106 +++++++++++++++ .../views/role_mappings/user.test.tsx | 123 +++++++++++++++++ .../views/role_mappings/user.tsx | 103 +++++++++++++++ 4 files changed, 456 insertions(+) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.test.tsx new file mode 100644 index 0000000000000..88103532bd149 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.test.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import '../../../__mocks__/react_router'; +import '../../../__mocks__/shallow_useeffect.mock'; +import { setMockActions, setMockValues } from '../../../__mocks__/kea_logic'; +import { engines } from '../../__mocks__/engines.mock'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { UserFlyout, UserAddedInfo, UserInvitationCallout } from '../../../shared/role_mapping'; +import { elasticsearchUsers } from '../../../shared/role_mapping/__mocks__/elasticsearch_users'; +import { wsSingleUserRoleMapping } from '../../../shared/role_mapping/__mocks__/roles'; + +import { EngineAssignmentSelector } from './engine_assignment_selector'; +import { User } from './user'; + +describe('User', () => { + const handleSaveUser = jest.fn(); + const closeUsersAndRolesFlyout = jest.fn(); + const setUserExistingRadioValue = jest.fn(); + const setElasticsearchUsernameValue = jest.fn(); + const setElasticsearchEmailValue = jest.fn(); + const handleRoleChange = jest.fn(); + const handleUsernameSelectChange = jest.fn(); + + const mockValues = { + availableEngines: [], + singleUserRoleMapping: null, + userFormUserIsExisting: false, + elasticsearchUsers: [], + elasticsearchUser: {}, + roleType: 'admin', + roleMappingErrors: [], + userCreated: false, + userFormIsNewUser: false, + hasAdvancedRoles: false, + }; + + beforeEach(() => { + setMockActions({ + handleSaveUser, + closeUsersAndRolesFlyout, + setUserExistingRadioValue, + setElasticsearchUsernameValue, + setElasticsearchEmailValue, + handleRoleChange, + handleUsernameSelectChange, + }); + + setMockValues(mockValues); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(UserFlyout)).toHaveLength(1); + }); + + it('renders engine assignment selector when groups present', () => { + setMockValues({ ...mockValues, availableEngines: engines, hasAdvancedRoles: true }); + const wrapper = shallow(); + + expect(wrapper.find(EngineAssignmentSelector)).toHaveLength(1); + }); + + it('renders userInvitationCallout', () => { + setMockValues({ + ...mockValues, + singleUserRoleMapping: wsSingleUserRoleMapping, + }); + const wrapper = shallow(); + + expect(wrapper.find(UserInvitationCallout)).toHaveLength(1); + }); + + it('renders user added info when user created', () => { + setMockValues({ + ...mockValues, + singleUserRoleMapping: wsSingleUserRoleMapping, + userCreated: true, + }); + const wrapper = shallow(); + + expect(wrapper.find(UserAddedInfo)).toHaveLength(1); + }); + + it('disables form when username value not present', () => { + setMockValues({ + ...mockValues, + singleUserRoleMapping: wsSingleUserRoleMapping, + elasticsearchUsers, + elasticsearchUser: { + username: null, + email: 'email@user.com', + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(UserFlyout).prop('disabled')).toEqual(true); + }); + + it('enables form when userFormUserIsExisting', () => { + setMockValues({ + ...mockValues, + userFormUserIsExisting: true.valueOf, + singleUserRoleMapping: wsSingleUserRoleMapping, + elasticsearchUsers, + elasticsearchUser: { + username: null, + email: 'email@user.com', + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(UserFlyout).prop('disabled')).toEqual(false); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.tsx new file mode 100644 index 0000000000000..df231fac64df7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.tsx @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useActions, useValues } from 'kea'; + +import { EuiForm } from '@elastic/eui'; + +import { getAppSearchUrl } from '../../../shared/enterprise_search_url'; +import { + UserFlyout, + UserSelector, + UserAddedInfo, + UserInvitationCallout, +} from '../../../shared/role_mapping'; +import { RoleTypes } from '../../types'; + +import { EngineAssignmentSelector } from './engine_assignment_selector'; +import { RoleMappingsLogic } from './role_mappings_logic'; + +const standardRoles = (['owner', 'admin'] as unknown) as RoleTypes[]; +const advancedRoles = (['dev', 'editor', 'analyst'] as unknown) as RoleTypes[]; + +export const User: React.FC = () => { + const { + handleSaveUser, + closeUsersAndRolesFlyout, + setUserExistingRadioValue, + setElasticsearchUsernameValue, + setElasticsearchEmailValue, + handleRoleChange, + handleUsernameSelectChange, + } = useActions(RoleMappingsLogic); + + const { + availableEngines, + singleUserRoleMapping, + hasAdvancedRoles, + userFormUserIsExisting, + elasticsearchUsers, + elasticsearchUser, + roleType, + roleMappingErrors, + userCreated, + userFormIsNewUser, + } = useValues(RoleMappingsLogic); + + const roleTypes = hasAdvancedRoles ? [...standardRoles, ...advancedRoles] : standardRoles; + const hasEngines = availableEngines.length > 0; + const showEngineAssignmentSelector = hasEngines && hasAdvancedRoles; + const flyoutDisabled = + !userFormUserIsExisting && (!elasticsearchUser.email || !elasticsearchUser.username); + + const userAddedInfo = singleUserRoleMapping && ( + + ); + + const userInvitationCallout = singleUserRoleMapping?.invitation && ( + + ); + + const createUserForm = ( + 0} error={roleMappingErrors}> + + {showEngineAssignmentSelector && } + + ); + + return ( + + {userCreated ? userAddedInfo : createUserForm} + {userInvitationCallout} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.test.tsx new file mode 100644 index 0000000000000..32ee1a7f22875 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.test.tsx @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import '../../../__mocks__/react_router'; +import '../../../__mocks__/shallow_useeffect.mock'; +import { setMockActions, setMockValues } from '../../../__mocks__/kea_logic'; +import { groups } from '../../__mocks__/groups.mock'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { UserFlyout, UserAddedInfo, UserInvitationCallout } from '../../../shared/role_mapping'; +import { elasticsearchUsers } from '../../../shared/role_mapping/__mocks__/elasticsearch_users'; +import { wsSingleUserRoleMapping } from '../../../shared/role_mapping/__mocks__/roles'; + +import { GroupAssignmentSelector } from './group_assignment_selector'; +import { User } from './user'; + +describe('User', () => { + const handleSaveUser = jest.fn(); + const closeUsersAndRolesFlyout = jest.fn(); + const setUserExistingRadioValue = jest.fn(); + const setElasticsearchUsernameValue = jest.fn(); + const setElasticsearchEmailValue = jest.fn(); + const handleRoleChange = jest.fn(); + const handleUsernameSelectChange = jest.fn(); + + const mockValues = { + availableGroups: [], + singleUserRoleMapping: null, + userFormUserIsExisting: false, + elasticsearchUsers: [], + elasticsearchUser: {}, + roleType: 'admin', + roleMappingErrors: [], + userCreated: false, + userFormIsNewUser: false, + }; + + beforeEach(() => { + setMockActions({ + handleSaveUser, + closeUsersAndRolesFlyout, + setUserExistingRadioValue, + setElasticsearchUsernameValue, + setElasticsearchEmailValue, + handleRoleChange, + handleUsernameSelectChange, + }); + + setMockValues(mockValues); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(UserFlyout)).toHaveLength(1); + }); + + it('renders group assignment selector when groups present', () => { + setMockValues({ ...mockValues, availableGroups: groups }); + const wrapper = shallow(); + + expect(wrapper.find(GroupAssignmentSelector)).toHaveLength(1); + }); + + it('renders userInvitationCallout', () => { + setMockValues({ + ...mockValues, + singleUserRoleMapping: wsSingleUserRoleMapping, + }); + const wrapper = shallow(); + + expect(wrapper.find(UserInvitationCallout)).toHaveLength(1); + }); + + it('renders user added info when user created', () => { + setMockValues({ + ...mockValues, + singleUserRoleMapping: wsSingleUserRoleMapping, + userCreated: true, + }); + const wrapper = shallow(); + + expect(wrapper.find(UserAddedInfo)).toHaveLength(1); + }); + + it('disables form when username value not present', () => { + setMockValues({ + ...mockValues, + singleUserRoleMapping: wsSingleUserRoleMapping, + elasticsearchUsers, + elasticsearchUser: { + username: null, + email: 'email@user.com', + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(UserFlyout).prop('disabled')).toEqual(true); + }); + + it('enables form when userFormUserIsExisting', () => { + setMockValues({ + ...mockValues, + userFormUserIsExisting: true.valueOf, + singleUserRoleMapping: wsSingleUserRoleMapping, + elasticsearchUsers, + elasticsearchUser: { + username: null, + email: 'email@user.com', + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(UserFlyout).prop('disabled')).toEqual(false); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.tsx new file mode 100644 index 0000000000000..bfb32ee31c121 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.tsx @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useActions, useValues } from 'kea'; + +import { EuiForm } from '@elastic/eui'; + +import { getWorkplaceSearchUrl } from '../../../shared/enterprise_search_url'; +import { + UserFlyout, + UserSelector, + UserAddedInfo, + UserInvitationCallout, +} from '../../../shared/role_mapping'; +import { Role } from '../../types'; + +import { GroupAssignmentSelector } from './group_assignment_selector'; +import { RoleMappingsLogic } from './role_mappings_logic'; + +const roleTypes = (['admin', 'user'] as unknown) as Role[]; + +export const User: React.FC = () => { + const { + handleSaveUser, + closeUsersAndRolesFlyout, + setUserExistingRadioValue, + setElasticsearchUsernameValue, + setElasticsearchEmailValue, + handleRoleChange, + handleUsernameSelectChange, + } = useActions(RoleMappingsLogic); + + const { + availableGroups, + singleUserRoleMapping, + userFormUserIsExisting, + elasticsearchUsers, + elasticsearchUser, + roleType, + roleMappingErrors, + userCreated, + userFormIsNewUser, + } = useValues(RoleMappingsLogic); + + const showGroupAssignmentSelector = availableGroups.length > 0; + const hasAvailableUsers = elasticsearchUsers.length > 0; + const flyoutDisabled = + (!userFormUserIsExisting || !hasAvailableUsers) && !elasticsearchUser.username; + + const userAddedInfo = singleUserRoleMapping && ( + + ); + + const userInvitationCallout = singleUserRoleMapping?.invitation && ( + + ); + + const createUserForm = ( + 0} error={roleMappingErrors}> + + {showGroupAssignmentSelector && } + + ); + + return ( + + {userCreated ? userAddedInfo : createUserForm} + {userInvitationCallout} + + ); +}; From cf8aad34a904d6064dead2cbb6ee2629f71f31ef Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 13:40:30 -0500 Subject: [PATCH 07/21] Add User list and User flyout to RoleMappings view --- .../role_mappings/role_mappings.test.tsx | 42 +++++++++++++++++-- .../role_mappings/role_mappings.tsx | 31 ++++++++++++++ .../role_mappings/role_mappings.test.tsx | 40 ++++++++++++++++-- .../views/role_mappings/role_mappings.tsx | 31 ++++++++++++++ 4 files changed, 137 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.test.tsx index 308022ccb2e5a..64bf41a50a2f0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.test.tsx @@ -12,26 +12,39 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { RoleMappingsTable, RoleMappingsHeading } from '../../../shared/role_mapping'; -import { wsRoleMapping } from '../../../shared/role_mapping/__mocks__/roles'; +import { + RoleMappingsTable, + RoleMappingsHeading, + UsersHeading, + UsersEmptyPrompt, +} from '../../../shared/role_mapping'; +import { + asRoleMapping, + asSingleUserRoleMapping, +} from '../../../shared/role_mapping/__mocks__/roles'; import { RoleMapping } from './role_mapping'; import { RoleMappings } from './role_mappings'; +import { User } from './user'; describe('RoleMappings', () => { const initializeRoleMappings = jest.fn(); const initializeRoleMapping = jest.fn(); + const initializeSingleUserRoleMapping = jest.fn(); const handleDeleteMapping = jest.fn(); const mockValues = { - roleMappings: [wsRoleMapping], + roleMappings: [asRoleMapping], dataLoading: false, multipleAuthProvidersConfig: false, + singleUserRoleMappings: [asSingleUserRoleMapping], + singleUserRoleMappingFlyoutOpen: false, }; beforeEach(() => { setMockActions({ initializeRoleMappings, initializeRoleMapping, + initializeSingleUserRoleMapping, handleDeleteMapping, }); setMockValues(mockValues); @@ -50,10 +63,31 @@ describe('RoleMappings', () => { expect(wrapper.find(RoleMapping)).toHaveLength(1); }); - it('handles onClick', () => { + it('renders User flyout', () => { + setMockValues({ ...mockValues, singleUserRoleMappingFlyoutOpen: true }); + const wrapper = shallow(); + + expect(wrapper.find(User)).toHaveLength(1); + }); + + it('handles RoleMappingsHeading onClick', () => { const wrapper = shallow(); wrapper.find(RoleMappingsHeading).prop('onClick')(); expect(initializeRoleMapping).toHaveBeenCalled(); }); + + it('handles UsersHeading onClick', () => { + const wrapper = shallow(); + wrapper.find(UsersHeading).prop('onClick')(); + + expect(initializeSingleUserRoleMapping).toHaveBeenCalled(); + }); + + it('handles empty users state', () => { + setMockValues({ ...mockValues, singleUserRoleMappings: [] }); + const wrapper = shallow(); + + expect(wrapper.find(UsersEmptyPrompt)).toHaveLength(1); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.tsx index 03e2ae67eca9e..3e692aa48623e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings.tsx @@ -9,11 +9,16 @@ import React, { useEffect } from 'react'; import { useActions, useValues } from 'kea'; +import { EuiSpacer } from '@elastic/eui'; + import { APP_SEARCH_PLUGIN } from '../../../../../common/constants'; import { RoleMappingsTable, RoleMappingsHeading, RolesEmptyPrompt, + UsersTable, + UsersHeading, + UsersEmptyPrompt, } from '../../../shared/role_mapping'; import { ROLE_MAPPINGS_TITLE } from '../../../shared/role_mapping/constants'; @@ -23,6 +28,7 @@ import { AppSearchPageTemplate } from '../layout'; import { ROLE_MAPPINGS_ENGINE_ACCESS_HEADING } from './constants'; import { RoleMapping } from './role_mapping'; import { RoleMappingsLogic } from './role_mappings_logic'; +import { User } from './user'; const ROLES_DOCS_LINK = `${DOCS_PREFIX}/security-and-users.html`; @@ -31,14 +37,17 @@ export const RoleMappings: React.FC = () => { enableRoleBasedAccess, initializeRoleMappings, initializeRoleMapping, + initializeSingleUserRoleMapping, handleDeleteMapping, resetState, } = useActions(RoleMappingsLogic); const { roleMappings, + singleUserRoleMappings, multipleAuthProvidersConfig, dataLoading, roleMappingFlyoutOpen, + singleUserRoleMappingFlyoutOpen, } = useValues(RoleMappingsLogic); useEffect(() => { @@ -46,6 +55,8 @@ export const RoleMappings: React.FC = () => { return resetState; }, []); + const hasUsers = singleUserRoleMappings.length > 0; + const rolesEmptyState = ( { ); + const usersTable = ( + + ); + + const usersSection = ( + <> + initializeSingleUserRoleMapping()} /> + + {hasUsers ? usersTable : } + + ); + return ( { emptyState={rolesEmptyState} > {roleMappingFlyoutOpen && } + {singleUserRoleMappingFlyoutOpen && } {roleMappingsSection} + + {usersSection} ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.test.tsx index 308022ccb2e5a..2e13f24a13eee 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.test.tsx @@ -12,26 +12,39 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { RoleMappingsTable, RoleMappingsHeading } from '../../../shared/role_mapping'; -import { wsRoleMapping } from '../../../shared/role_mapping/__mocks__/roles'; +import { + RoleMappingsTable, + RoleMappingsHeading, + UsersHeading, + UsersEmptyPrompt, +} from '../../../shared/role_mapping'; +import { + wsRoleMapping, + wsSingleUserRoleMapping, +} from '../../../shared/role_mapping/__mocks__/roles'; import { RoleMapping } from './role_mapping'; import { RoleMappings } from './role_mappings'; +import { User } from './user'; describe('RoleMappings', () => { const initializeRoleMappings = jest.fn(); const initializeRoleMapping = jest.fn(); + const initializeSingleUserRoleMapping = jest.fn(); const handleDeleteMapping = jest.fn(); const mockValues = { roleMappings: [wsRoleMapping], dataLoading: false, multipleAuthProvidersConfig: false, + singleUserRoleMappings: [wsSingleUserRoleMapping], + singleUserRoleMappingFlyoutOpen: false, }; beforeEach(() => { setMockActions({ initializeRoleMappings, initializeRoleMapping, + initializeSingleUserRoleMapping, handleDeleteMapping, }); setMockValues(mockValues); @@ -50,10 +63,31 @@ describe('RoleMappings', () => { expect(wrapper.find(RoleMapping)).toHaveLength(1); }); - it('handles onClick', () => { + it('renders User flyout', () => { + setMockValues({ ...mockValues, singleUserRoleMappingFlyoutOpen: true }); + const wrapper = shallow(); + + expect(wrapper.find(User)).toHaveLength(1); + }); + + it('handles RoleMappingsHeading onClick', () => { const wrapper = shallow(); wrapper.find(RoleMappingsHeading).prop('onClick')(); expect(initializeRoleMapping).toHaveBeenCalled(); }); + + it('handles UsersHeading onClick', () => { + const wrapper = shallow(); + wrapper.find(UsersHeading).prop('onClick')(); + + expect(initializeSingleUserRoleMapping).toHaveBeenCalled(); + }); + + it('handles empty users state', () => { + setMockValues({ ...mockValues, singleUserRoleMappings: [] }); + const wrapper = shallow(); + + expect(wrapper.find(UsersEmptyPrompt)).toHaveLength(1); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.tsx index 01d32bec14ebd..df5d7e4267690 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings.tsx @@ -9,11 +9,16 @@ import React, { useEffect } from 'react'; import { useActions, useValues } from 'kea'; +import { EuiSpacer } from '@elastic/eui'; + import { WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants'; import { RoleMappingsTable, RoleMappingsHeading, RolesEmptyPrompt, + UsersTable, + UsersHeading, + UsersEmptyPrompt, } from '../../../shared/role_mapping'; import { ROLE_MAPPINGS_TITLE } from '../../../shared/role_mapping/constants'; import { WorkplaceSearchPageTemplate } from '../../components/layout'; @@ -23,26 +28,32 @@ import { ROLE_MAPPINGS_TABLE_HEADER } from './constants'; import { RoleMapping } from './role_mapping'; import { RoleMappingsLogic } from './role_mappings_logic'; +import { User } from './user'; export const RoleMappings: React.FC = () => { const { enableRoleBasedAccess, initializeRoleMappings, initializeRoleMapping, + initializeSingleUserRoleMapping, handleDeleteMapping, } = useActions(RoleMappingsLogic); const { roleMappings, + singleUserRoleMappings, dataLoading, multipleAuthProvidersConfig, roleMappingFlyoutOpen, + singleUserRoleMappingFlyoutOpen, } = useValues(RoleMappingsLogic); useEffect(() => { initializeRoleMappings(); }, []); + const hasUsers = singleUserRoleMappings.length > 0; + const rolesEmptyState = ( { ); + const usersTable = ( + + ); + + const usersSection = ( + <> + initializeSingleUserRoleMapping()} /> + + {hasUsers ? usersTable : } + + ); + return ( { emptyState={rolesEmptyState} > {roleMappingFlyoutOpen && } + {singleUserRoleMappingFlyoutOpen && } {roleMappingsSection} + + {usersSection} ); }; From c668bef14514df2dcdb37c076fc7edc3eaa2acb9 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 13:41:59 -0500 Subject: [PATCH 08/21] Fix path --- .../components/role_mappings/role_mappings_logic.test.ts | 2 +- .../views/role_mappings/role_mappings_logic.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts index 311abadf6b579..16b44e9ec1f11 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.test.ts @@ -15,7 +15,7 @@ import { engines } from '../../__mocks__/engines.mock'; import { nextTick } from '@kbn/test/jest'; -import { elasticsearchUsers } from '../../../shared/role_mapping/__mocks__/elasticsearchUsers'; +import { elasticsearchUsers } from '../../../shared/role_mapping/__mocks__/elasticsearch_users'; import { asRoleMapping, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts index ac0536a4988c5..c85e86ebcca2c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.test.ts @@ -15,7 +15,7 @@ import { groups } from '../../__mocks__/groups.mock'; import { nextTick } from '@kbn/test/jest'; -import { elasticsearchUsers } from '../../../shared/role_mapping/__mocks__/elasticsearchUsers'; +import { elasticsearchUsers } from '../../../shared/role_mapping/__mocks__/elasticsearch_users'; import { wsRoleMapping, From 0f6977efaef033d1798eb87e00acdcee8bca0874 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 13:44:10 -0500 Subject: [PATCH 09/21] Rename things - Users & roles -> Users and roles - roleId -> roleMappingId (matches other places in code) - also added a missing prop to the actions col --- .../applications/app_search/components/layout/nav.test.tsx | 2 +- .../public/applications/shared/role_mapping/constants.ts | 2 +- .../public/applications/shared/role_mapping/users_table.tsx | 5 +++-- .../workplace_search/components/layout/nav.test.tsx | 2 +- .../public/applications/workplace_search/constants.ts | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx index c9f5452e254e1..68faa4298c2a3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx @@ -100,7 +100,7 @@ describe('useAppSearchNav', () => { }, { id: 'usersRoles', - name: 'Users & roles', + name: 'Users and roles', href: '/role_mappings', }, ], diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts index 45cab32b67e08..cb0ce731907a7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts @@ -136,7 +136,7 @@ export const FILTER_ROLE_MAPPINGS_PLACEHOLDER = i18n.translate( export const ROLE_MAPPINGS_TITLE = i18n.translate( 'xpack.enterpriseSearch.roleMapping.roleMappingsTitle', { - defaultMessage: 'Users & roles', + defaultMessage: 'Users and roles', } ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/users_table.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/users_table.tsx index 86dc2c2626229..674796775b1d3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/users_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/users_table.tsx @@ -46,8 +46,8 @@ interface SharedRoleMapping extends ASRoleMapping, WSRoleMapping { interface Props { accessItemKey: 'groups' | 'engines'; singleUserRoleMappings: Array>; - initializeSingleUserRoleMapping(roleId: string): string; - handleDeleteMapping(roleId: string): string; + initializeSingleUserRoleMapping(roleMappingId: string): void; + handleDeleteMapping(roleMappingId: string): void; } const noItemsPlaceholder = ; @@ -110,6 +110,7 @@ export const UsersTable: React.FC = ({ { field: 'id', name: '', + align: 'right', render: (_, { id, username }: SharedUser) => ( { }, { id: 'usersRoles', - name: 'Users & roles', + name: 'Users and roles', href: '/role_mappings', }, { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts index aa5419f12c7f3..cf459171a808a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts @@ -40,7 +40,7 @@ export const NAV = { defaultMessage: 'Content', }), ROLE_MAPPINGS: i18n.translate('xpack.enterpriseSearch.workplaceSearch.nav.roleMappings', { - defaultMessage: 'Users & roles', + defaultMessage: 'Users and roles', }), SECURITY: i18n.translate('xpack.enterpriseSearch.workplaceSearch.nav.security', { defaultMessage: 'Security', From ebfea977e4288f9b33dec9db9406be9d69cf6906 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 13:46:57 -0500 Subject: [PATCH 10/21] Set default group when modal closed The UI sets the default group on page load but did not cover the case where the user has chosen a group in a previous interaction and the closed the flyout. This commit adds a method that resets that state when the flyout is closed Part of porting of https://github.com/elastic/ent-search/pull/3865 Specifically: https://github.com/elastic/ent-search/commit/a4131b95dab7c0df97bd78e660f25e09ac3e7cec --- .../views/role_mappings/role_mappings_logic.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts index add71c85c252b..6143108031191 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts @@ -74,6 +74,7 @@ interface RoleMappingsActions { setElasticsearchUser( elasticsearchUser?: ElasticsearchUser ): { elasticsearchUser: ElasticsearchUser }; + setDefaultGroup(availableGroups: RoleGroup[]): { availableGroups: RoleGroup[] }; openRoleMappingFlyout(): void; openSingleUserRoleMappingFlyout(): void; closeUsersAndRolesFlyout(): void; @@ -143,6 +144,7 @@ export const RoleMappingsLogic = kea ({ roleMappingId }), handleSaveMapping: true, handleSaveUser: true, + setDefaultGroup: (availableGroups: RoleGroup[]) => ({ availableGroups }), openRoleMappingFlyout: true, closeUsersAndRolesFlyout: false, setElasticsearchUsernameValue: (username: string) => ({ username }), @@ -266,6 +268,12 @@ export const RoleMappingsLogic = kea group.name === DEFAULT_GROUP_NAME) .map((group) => group.id) ), + setDefaultGroup: (_, { availableGroups }) => + new Set( + availableGroups + .filter((group) => group.name === DEFAULT_GROUP_NAME) + .map((group) => group.id) + ), setRoleMapping: (_, { roleMapping }) => new Set(roleMapping.groups.map((group: RoleGroup) => group.id)), handleGroupSelectionChange: (_, { groupIds }) => { @@ -503,6 +511,7 @@ export const RoleMappingsLogic = kea { clearFlashMessages(); From cb7307eff59a493225530bfb5714f766a9405156 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 13:48:10 -0500 Subject: [PATCH 11/21] Adds tooltip for external attribute This was missed from the design Part of porting of https://github.com/elastic/ent-search/pull/3865 Specifically: https://github.com/elastic/ent-search/commit/03aa349cab4fb32069b64ab8c51a7252ba52e805 --- .../applications/shared/role_mapping/constants.ts | 8 ++++++++ .../shared/role_mapping/role_mappings_table.tsx | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts index cb0ce731907a7..7ae1d70110cac 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts @@ -406,3 +406,11 @@ export const FILTER_USERS_LABEL = i18n.translate( export const NO_USERS_LABEL = i18n.translate('xpack.enterpriseSearch.roleMapping.noUsersLabel', { defaultMessage: 'No matching users found', }); + +export const EXTERNAL_ATTRIBUTE_TOOLTIP = i18n.translate( + 'xpack.enterpriseSearch.roleMapping.externalAttributeTooltip', + { + defaultMessage: + 'External attributes are defined by the identity provider, and varies from service to service.', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx index eb9621c7a242c..7872414278231 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx @@ -25,6 +25,7 @@ import { ATTRIBUTE_VALUE_LABEL, FILTER_ROLE_MAPPINGS_PLACEHOLDER, ROLE_MAPPINGS_NO_RESULTS_MESSAGE, + EXTERNAL_ATTRIBUTE_TOOLTIP, } from './constants'; import { UsersAndRolesRowActions } from './users_and_roles_row_actions'; @@ -69,7 +70,19 @@ export const RoleMappingsTable: React.FC = ({ const attributeNameCol: EuiBasicTableColumn = { field: 'attribute', - name: EXTERNAL_ATTRIBUTE_LABEL, + name: ( + + {EXTERNAL_ATTRIBUTE_LABEL}{' '} + + + ), render: (_, { rules }: SharedRoleMapping) => getFirstAttributeName(rules), }; From 2fcccc31842078a809fa7f644103ca286bda6ad0 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 13:48:41 -0500 Subject: [PATCH 12/21] Fix invitations link --- .../shared/role_mapping/user_invitation_callout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_invitation_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_invitation_callout.tsx index 8310077ad6f2e..d6d0ce7b050ab 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_invitation_callout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_invitation_callout.tsx @@ -23,7 +23,7 @@ interface Props { } export const UserInvitationCallout: React.FC = ({ isNew, invitationCode, urlPrefix }) => { - const link = urlPrefix + invitationCode; + const link = `${urlPrefix}/invitations/${invitationCode}`; const label = isNew ? NEW_INVITATION_LABEL : EXISTING_INVITATION_LABEL; return ( From 80c4f0e52c24cb5589e987237af045268aa9e971 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 13:52:18 -0500 Subject: [PATCH 13/21] Fix incorrect role type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Role-> RoleTypes 🤷🏽‍♀️ --- .../public/applications/shared/role_mapping/user_selector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx index 70348bf29894a..fd9163321f070 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx @@ -16,7 +16,7 @@ import { EuiSpacer, } from '@elastic/eui'; -import { Role as ASRole } from '../../app_search/types'; +import { RoleTypes as ASRole } from '../../app_search/types'; import { ElasticsearchUser } from '../../shared/types'; import { Role as WSRole } from '../../workplace_search/types'; From 9d1fb67172510c1814932049989934d14096f9f6 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 13:54:59 -0500 Subject: [PATCH 14/21] Add EuiPortal to Flyout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wasn’t needed in ent-search; already done for RomeMappingFlyout. Hide whitespace changes plskthx --- .../shared/role_mapping/user_flyout.tsx | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_flyout.tsx index e13a56a716929..a3be5e295ddfe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_flyout.tsx @@ -17,6 +17,7 @@ import { EuiFlyoutFooter, EuiFlyoutHeader, EuiIcon, + EuiPortal, EuiText, EuiTitle, EuiSpacer, @@ -92,22 +93,26 @@ export const UserFlyout: React.FC = ({ ); return ( - - - -

{isComplete ? IS_COMPLETE_HEADING : IS_EDITING_HEADING}

-
- {!isComplete && ( - -

{IS_EDITING_DESCRIPTION}

-
- )} -
- - {children} - - - {isComplete ? completedFooterAction : editingFooterActions} -
+ + + + +

{isComplete ? IS_COMPLETE_HEADING : IS_EDITING_HEADING}

+
+ {!isComplete && ( + +

{IS_EDITING_DESCRIPTION}

+
+ )} +
+ + {children} + + + + {isComplete ? completedFooterAction : editingFooterActions} + +
+
); }; From cac8cd56a1513d6f234e9147af2d98d45ac261b2 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 14:15:49 -0500 Subject: [PATCH 15/21] Auth provider deprecation warning in mapping UI Since we're moving fully into Kibana, we're losing our concept of auth providers. In 8.0, role mappings the specify an auth provider will no longer work, so this adds a small deprecation warning in the role mappings table. https://github.com/elastic/ent-search/pull/3885 --- .../shared/role_mapping/constants.ts | 8 ++++++ .../role_mapping/role_mappings_table.test.tsx | 10 +++++--- .../role_mapping/role_mappings_table.tsx | 25 +++++++++++++------ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts index 7ae1d70110cac..215c76ffb7ef4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts @@ -414,3 +414,11 @@ export const EXTERNAL_ATTRIBUTE_TOOLTIP = i18n.translate( 'External attributes are defined by the identity provider, and varies from service to service.', } ); + +export const AUTH_PROVIDER_TOOLTIP = i18n.translate( + 'xpack.enterpriseSearch.roleMapping.authProviderTooltip', + { + defaultMessage: + 'Provider-specific role mapping is still applied, but configuration is now deprecated.', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.test.tsx index 81a7c06020165..c8cc2673a387e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.test.tsx @@ -59,11 +59,13 @@ describe('RoleMappingsTable', () => { }); it('renders auth provider display names', () => { - const wrapper = mount(); + const roleMappingWithAuths = { + ...wsRoleMapping, + authProvider: ['saml', 'native'], + }; + const wrapper = mount(); - expect(wrapper.find('[data-test-subj="AuthProviderDisplayValue"]').prop('children')).toEqual( - `${ANY_AUTH_PROVIDER_OPTION_LABEL}, other_auth` - ); + expect(wrapper.find('[data-test-subj="ProviderSpecificList"]')).toHaveLength(1); }); it('handles manage click', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx index 7872414278231..90641bd0e7623 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx @@ -11,10 +11,13 @@ import { EuiIconTip, EuiInMemoryTable, EuiBasicTableColumn } from '@elastic/eui' import { ASRoleMapping } from '../../app_search/types'; import { WSRoleMapping } from '../../workplace_search/types'; +import { docLinks } from '../doc_links'; import { RoleRules } from '../types'; import './role_mappings_table.scss'; +const AUTH_PROVIDER_DOCUMENTATION_URL = `${docLinks.enterpriseSearchBase}/users-access.html`; + import { ANY_AUTH_PROVIDER, ANY_AUTH_PROVIDER_OPTION_LABEL, @@ -26,6 +29,7 @@ import { FILTER_ROLE_MAPPINGS_PLACEHOLDER, ROLE_MAPPINGS_NO_RESULTS_MESSAGE, EXTERNAL_ATTRIBUTE_TOOLTIP, + AUTH_PROVIDER_TOOLTIP, } from './constants'; import { UsersAndRolesRowActions } from './users_and_roles_row_actions'; @@ -47,9 +51,6 @@ interface Props { handleDeleteMapping(roleMappingId: string): void; } -const getAuthProviderDisplayValue = (authProvider: string) => - authProvider === ANY_AUTH_PROVIDER ? ANY_AUTH_PROVIDER_OPTION_LABEL : authProvider; - export const RoleMappingsTable: React.FC = ({ accessItemKey, accessHeader, @@ -118,11 +119,19 @@ export const RoleMappingsTable: React.FC = ({ const authProviderCol: EuiBasicTableColumn = { field: 'authProvider', name: AUTH_PROVIDER_LABEL, - render: (_, { authProvider }: SharedRoleMapping) => ( - - {authProvider.map(getAuthProviderDisplayValue).join(', ')} - - ), + render: (_, { authProvider }: SharedRoleMapping) => { + if (authProvider[0] === ANY_AUTH_PROVIDER) { + return ANY_AUTH_PROVIDER_OPTION_LABEL; + } + return ( + + {authProvider.join(', ')}{' '} + + + + + ); + }, }; const actionsCol: EuiBasicTableColumn = { From 08e954d598a81abf0baf1e8056cfc2ffa816b42a Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 14:25:58 -0500 Subject: [PATCH 16/21] Email is no longer required After a slack discussion, it was determined that email should be optional. This commit also fixes another instance of the App Search role type being wrong. --- .../role_mapping/user_added_info.test.tsx | 100 +++++++++++++++++- .../shared/role_mapping/user_added_info.tsx | 6 +- .../role_mapping/user_selector.test.tsx | 3 +- .../shared/role_mapping/user_selector.tsx | 2 +- 4 files changed, 102 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.test.tsx index 30bdaa0010b58..57200b389591d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.test.tsx @@ -9,8 +9,6 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiText } from '@elastic/eui'; - import { UserAddedInfo } from './'; describe('UserAddedInfo', () => { @@ -20,9 +18,103 @@ describe('UserAddedInfo', () => { roleType: 'user', }; - it('renders', () => { + it('renders with email', () => { const wrapper = shallow(); - expect(wrapper.find(EuiText)).toHaveLength(6); + expect(wrapper).toMatchInlineSnapshot(` + + + + Username + + + + user1 + + + + + Email + + + + test@test.com + + + + + Role + + + + user + + + + `); + }); + + it('renders without email', () => { + const wrapper = shallow(); + + expect(wrapper).toMatchInlineSnapshot(` + + + + Username + + + + user1 + + + + + Email + + + + + — + + + + + + Role + + + + user + + + + `); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.tsx index a12eae66262a0..37804414a94a9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import { EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiSpacer, EuiText, EuiTextColor } from '@elastic/eui'; import { USERNAME_LABEL, EMAIL_LABEL } from '../constants'; @@ -19,6 +19,8 @@ interface Props { roleType: string; } +const noItemsPlaceholder = ; + export const UserAddedInfo: React.FC = ({ username, email, roleType }) => ( <> @@ -29,7 +31,7 @@ export const UserAddedInfo: React.FC = ({ username, email, roleType }) => {EMAIL_LABEL} - {email} + {email || noItemsPlaceholder} {ROLE_LABEL} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.test.tsx index 08ddc7ba5427f..60bac97d09835 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.test.tsx @@ -13,7 +13,7 @@ import { shallow } from 'enzyme'; import { EuiFormRow } from '@elastic/eui'; -import { Role as ASRole } from '../../app_search/types'; +import { RoleTypes as ASRole } from '../../app_search/types'; import { REQUIRED_LABEL, USERNAME_NO_USERS_TEXT } from './constants'; @@ -107,6 +107,5 @@ describe('UserSelector', () => { expect(wrapper.find(EuiFormRow).at(0).prop('helpText')).toEqual(USERNAME_NO_USERS_TEXT); expect(wrapper.find(EuiFormRow).at(1).prop('helpText')).toEqual(REQUIRED_LABEL); - expect(wrapper.find(EuiFormRow).at(2).prop('helpText')).toEqual(REQUIRED_LABEL); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx index fd9163321f070..93d82bd494a9d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx @@ -80,7 +80,7 @@ export const UserSelector: React.FC = ({ ); const emailInput = ( - + Date: Wed, 23 Jun 2021 14:31:38 -0500 Subject: [PATCH 17/21] =?UTF-8?q?Existing=20users=E2=80=99=20usernames=20s?= =?UTF-8?q?hould=20not=20be=20editable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../public/applications/shared/role_mapping/user_selector.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx index 93d82bd494a9d..d65f97265f6a3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_selector.tsx @@ -95,6 +95,7 @@ export const UserSelector: React.FC = ({ setElasticsearchUsernameValue(e.target.value)} From 9d85270aaf26cec780643b6814f783ac0c1b590a Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 15:09:33 -0500 Subject: [PATCH 18/21] Use EuiLink instead of anchor --- .../shared/role_mapping/role_mappings_table.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx index 90641bd0e7623..4136d114d3420 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import { EuiIconTip, EuiInMemoryTable, EuiBasicTableColumn } from '@elastic/eui'; +import { EuiIconTip, EuiInMemoryTable, EuiBasicTableColumn, EuiLink } from '@elastic/eui'; import { ASRoleMapping } from '../../app_search/types'; import { WSRoleMapping } from '../../workplace_search/types'; @@ -126,9 +126,9 @@ export const RoleMappingsTable: React.FC = ({ return ( {authProvider.join(', ')}{' '} - + - + ); }, From ae4c9bffea13e27ab18ed4a336b61c45fe588021 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 15:38:07 -0500 Subject: [PATCH 19/21] Add validation tests --- .../routes/app_search/role_mappings.test.ts | 25 +++++++++++++++++++ .../workplace_search/role_mappings.test.ts | 25 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts index fe9c1c80dae4c..dfb9765f834b6 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/role_mappings.test.ts @@ -178,6 +178,31 @@ describe('role mappings routes', () => { }); }); + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + roleMapping: { + engines: ['foo', 'bar'], + roleType: 'admin', + accessAllEngines: true, + id: '123asf', + }, + elasticsearchUser: { + username: 'user2@elastic.co', + email: 'user2', + }, + }, + }; + mockRouter.shouldValidate(request); + }); + + it('missing required fields', () => { + const request = { body: {} }; + mockRouter.shouldThrow(request); + }); + }); + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/as/role_mappings/upsert_single_user_role_mapping', diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts index abaf1d6439ef1..ef8f1bd63f5d3 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/role_mappings.test.ts @@ -146,6 +146,31 @@ describe('role mappings routes', () => { }); }); + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + roleMapping: { + groups: ['foo', 'bar'], + roleType: 'admin', + allGroups: true, + id: '123asf', + }, + elasticsearchUser: { + username: 'user2@elastic.co', + email: 'user2', + }, + }, + }; + mockRouter.shouldValidate(request); + }); + + it('missing required fields', () => { + const request = { body: {} }; + mockRouter.shouldThrow(request); + }); + }); + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/role_mappings/upsert_single_user_role_mapping', From 92e493ea79b0d04e6d0871bffd97dd51cf4388fd Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 15:44:08 -0500 Subject: [PATCH 20/21] Change URL for users_and_roles Need to change folder and file names but will punt until after 7.14FF I did throw in updating the logic file path --- .../applications/app_search/components/layout/nav.test.tsx | 2 +- .../applications/app_search/components/layout/nav.tsx | 4 ++-- .../components/role_mappings/role_mappings_logic.ts | 2 +- .../public/applications/app_search/index.test.tsx | 2 +- .../public/applications/app_search/index.tsx | 6 +++--- .../public/applications/app_search/routes.ts | 2 +- .../workplace_search/components/layout/nav.test.tsx | 2 +- .../applications/workplace_search/components/layout/nav.tsx | 6 +++--- .../public/applications/workplace_search/index.tsx | 4 ++-- .../public/applications/workplace_search/routes.ts | 2 +- .../views/role_mappings/role_mappings_logic.ts | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx index 68faa4298c2a3..ce4a118bef095 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.test.tsx @@ -101,7 +101,7 @@ describe('useAppSearchNav', () => { { id: 'usersRoles', name: 'Users and roles', - href: '/role_mappings', + href: '/users_and_roles', }, ], }, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.tsx index c3b8ec642233b..793a36f48fe82 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/nav.tsx @@ -13,7 +13,7 @@ import { generateNavLink } from '../../../shared/layout'; import { ROLE_MAPPINGS_TITLE } from '../../../shared/role_mapping/constants'; import { AppLogic } from '../../app_logic'; -import { ENGINES_PATH, SETTINGS_PATH, CREDENTIALS_PATH, ROLE_MAPPINGS_PATH } from '../../routes'; +import { ENGINES_PATH, SETTINGS_PATH, CREDENTIALS_PATH, USERS_AND_ROLES_PATH } from '../../routes'; import { CREDENTIALS_TITLE } from '../credentials'; import { useEngineNav } from '../engine/engine_nav'; import { ENGINES_TITLE } from '../engines'; @@ -57,7 +57,7 @@ export const useAppSearchNav = () => { navItems.push({ id: 'usersRoles', name: ROLE_MAPPINGS_TITLE, - ...generateNavLink({ to: ROLE_MAPPINGS_PATH }), + ...generateNavLink({ to: USERS_AND_ROLES_PATH }), }); } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts index c65692251c5ca..0b57e1d08a294 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/role_mappings_logic.ts @@ -118,7 +118,7 @@ interface RoleMappingsValues { } export const RoleMappingsLogic = kea>({ - path: ['enterprise_search', 'app_search', 'role_mappings'], + path: ['enterprise_search', 'app_search', 'users_and_roles'], actions: { setRoleMappingsData: (data: RoleMappingsServerDetails) => data, setRoleMapping: (roleMapping: ASRoleMapping) => ({ roleMapping }), diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx index 2402a6ecc6401..00acea945177a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx @@ -196,6 +196,6 @@ describe('AppSearchNav', () => { setMockValues({ myRole: { canViewRoleMappings: true } }); const wrapper = shallow(); - expect(wrapper.find(SideNavLink).last().prop('to')).toEqual('/role_mappings'); + expect(wrapper.find(SideNavLink).last().prop('to')).toEqual('/users_and_roles'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx index 191758af26758..d7ddad5683f38 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx @@ -37,7 +37,7 @@ import { SETUP_GUIDE_PATH, SETTINGS_PATH, CREDENTIALS_PATH, - ROLE_MAPPINGS_PATH, + USERS_AND_ROLES_PATH, ENGINES_PATH, ENGINE_PATH, LIBRARY_PATH, @@ -128,7 +128,7 @@ export const AppSearchConfigured: React.FC> = (props) = )} {canViewRoleMappings && ( - + )} @@ -162,7 +162,7 @@ export const AppSearchNav: React.FC = () => { {CREDENTIALS_TITLE} )} {canViewRoleMappings && ( - + {ROLE_MAPPINGS_TITLE} )} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts index d9d1935c648f7..f086a32bbf590 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts @@ -15,7 +15,7 @@ export const LIBRARY_PATH = '/library'; export const SETTINGS_PATH = '/settings'; export const CREDENTIALS_PATH = '/credentials'; -export const ROLE_MAPPINGS_PATH = '/role_mappings'; +export const USERS_AND_ROLES_PATH = '/users_and_roles'; export const ENGINES_PATH = '/engines'; export const ENGINE_CREATION_PATH = `${ENGINES_PATH}/new`; // This is safe from conflicting with an :engineName path because new is a reserved name diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx index 3526bd2541e70..04576e981e104 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx @@ -54,7 +54,7 @@ describe('useWorkplaceSearchNav', () => { { id: 'usersRoles', name: 'Users and roles', - href: '/role_mappings', + href: '/users_and_roles', }, { id: 'security', diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx index ce2f8bf7ef7e4..c8d821dcdae2e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx @@ -15,7 +15,7 @@ import { NAV } from '../../constants'; import { SOURCES_PATH, SECURITY_PATH, - ROLE_MAPPINGS_PATH, + USERS_AND_ROLES_PATH, GROUPS_PATH, ORG_SETTINGS_PATH, } from '../../routes'; @@ -48,7 +48,7 @@ export const useWorkplaceSearchNav = () => { { id: 'usersRoles', name: NAV.ROLE_MAPPINGS, - ...generateNavLink({ to: ROLE_MAPPINGS_PATH }), + ...generateNavLink({ to: USERS_AND_ROLES_PATH }), }, { id: 'security', @@ -92,7 +92,7 @@ export const WorkplaceSearchNav: React.FC = ({ {NAV.GROUPS} - + {NAV.ROLE_MAPPINGS} {NAV.SECURITY} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx index 8a1e9c0275322..05018be2934b4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx @@ -26,7 +26,7 @@ import { SOURCE_ADDED_PATH, PERSONAL_SOURCES_PATH, ORG_SETTINGS_PATH, - ROLE_MAPPINGS_PATH, + USERS_AND_ROLES_PATH, SECURITY_PATH, PERSONAL_SETTINGS_PATH, PERSONAL_PATH, @@ -103,7 +103,7 @@ export const WorkplaceSearchConfigured: React.FC = (props) => { - + diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts index 3c564c1f912ec..b9309ffd94809 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/routes.ts @@ -48,7 +48,7 @@ export const ENT_SEARCH_LICENSE_MANAGEMENT = `${docLinks.enterpriseSearchBase}/l export const PERSONAL_PATH = '/p'; -export const ROLE_MAPPINGS_PATH = '/role_mappings'; +export const USERS_AND_ROLES_PATH = '/users_and_roles'; export const USERS_PATH = '/users'; export const SECURITY_PATH = '/security'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts index 6143108031191..7f26c8738786c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/role_mappings_logic.ts @@ -116,7 +116,7 @@ interface RoleMappingsValues { } export const RoleMappingsLogic = kea>({ - path: ['enterprise_search', 'workplace_search', 'role_mappings'], + path: ['enterprise_search', 'workplace_search', 'users_and_roles'], actions: { setRoleMappingsData: (data: RoleMappingsServerDetails) => data, setRoleMapping: (roleMapping: WSRoleMapping) => ({ roleMapping }), From ea7776a9033e8f7767fd40edd977a3825c55dd35 Mon Sep 17 00:00:00 2001 From: scottybollinger Date: Wed, 23 Jun 2021 16:07:58 -0500 Subject: [PATCH 21/21] Remove unused import --- .../shared/role_mapping/role_mappings_table.test.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.test.tsx index c8cc2673a387e..61043aa6ad9a8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/role_mappings_table.test.tsx @@ -15,8 +15,6 @@ import { EuiInMemoryTable, EuiTableHeaderCell } from '@elastic/eui'; import { engines } from '../../app_search/__mocks__/engines.mock'; -import { ANY_AUTH_PROVIDER_OPTION_LABEL } from './constants'; - import { RoleMappingsTable } from './role_mappings_table'; import { UsersAndRolesRowActions } from './users_and_roles_row_actions';