Skip to content

Commit

Permalink
[Enterprise Search] Add User management feature (#103173) (#103267)
Browse files Browse the repository at this point in the history
* Rename method to close both flyouts

This is shared with the forthcoming user flyouts

closeRoleMappingFlyout -> closeUsersAndRolesFlyout

* Add logic for elasticsearch users and single user role mappings

* 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

* Add User server routes

* Add logic for saving a user

* Add User components

* Add User list and User flyout to RoleMappings view

* Fix path

* Rename things

- Users & roles -> Users and roles
- roleId -> roleMappingId (matches other places in code)
- also added a missing prop to the actions col

* 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 elastic/ent-search#3865

Specifically:
elastic/ent-search@a4131b9

* Adds tooltip for external attribute

This was missed from the design

Part of porting of elastic/ent-search#3865

Specifically:
elastic/ent-search@03aa349

* Fix invitations link

* Fix incorrect role type

Role-> RoleTypes
🤷🏽‍♀️

* Add EuiPortal to Flyout

Wasn’t needed in ent-search; already done for RomeMappingFlyout. Hide whitespace changes plskthx

* 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.

elastic/ent-search#3885

* 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.

* Existing users’ usernames should not be editable

* Use EuiLink instead of anchor

* Add validation tests

* 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

* Remove unused import

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Scotty Bollinger <scotty.bollinger@elastic.co>
  • Loading branch information
kibanamachine and scottybollinger authored Jun 24, 2021
1 parent 6d44019 commit 4b546bf
Show file tree
Hide file tree
Showing 40 changed files with 1,868 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ describe('useAppSearchNav', () => {
},
{
id: 'usersRoles',
name: 'Users & roles',
href: '/role_mappings',
name: 'Users and roles',
href: '/users_and_roles',
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 }),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const RoleMapping: React.FC = () => {
handleAuthProviderChange,
handleRoleChange,
handleSaveMapping,
closeRoleMappingFlyout,
closeUsersAndRolesFlyout,
} = useActions(RoleMappingsLogic);

const {
Expand Down Expand Up @@ -68,7 +68,7 @@ export const RoleMapping: React.FC = () => {
<RoleMappingFlyout
disabled={attributeValueInvalid || !hasEngineAssignment}
isNew={isNew}
closeRoleMappingFlyout={closeRoleMappingFlyout}
closeUsersAndRolesFlyout={closeUsersAndRolesFlyout}
handleSaveMapping={handleSaveMapping}
>
<EuiForm isInvalid={roleMappingErrors.length > 0} error={roleMappingErrors}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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(<RoleMappings />);

expect(wrapper.find(User)).toHaveLength(1);
});

it('handles RoleMappingsHeading onClick', () => {
const wrapper = shallow(<RoleMappings />);
wrapper.find(RoleMappingsHeading).prop('onClick')();

expect(initializeRoleMapping).toHaveBeenCalled();
});

it('handles UsersHeading onClick', () => {
const wrapper = shallow(<RoleMappings />);
wrapper.find(UsersHeading).prop('onClick')();

expect(initializeSingleUserRoleMapping).toHaveBeenCalled();
});

it('handles empty users state', () => {
setMockValues({ ...mockValues, singleUserRoleMappings: [] });
const wrapper = shallow(<RoleMappings />);

expect(wrapper.find(UsersEmptyPrompt)).toHaveLength(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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`;

Expand All @@ -31,21 +37,26 @@ export const RoleMappings: React.FC = () => {
enableRoleBasedAccess,
initializeRoleMappings,
initializeRoleMapping,
initializeSingleUserRoleMapping,
handleDeleteMapping,
resetState,
} = useActions(RoleMappingsLogic);
const {
roleMappings,
singleUserRoleMappings,
multipleAuthProvidersConfig,
dataLoading,
roleMappingFlyoutOpen,
singleUserRoleMappingFlyoutOpen,
} = useValues(RoleMappingsLogic);

useEffect(() => {
initializeRoleMappings();
return resetState;
}, []);

const hasUsers = singleUserRoleMappings.length > 0;

const rolesEmptyState = (
<RolesEmptyPrompt
productName={APP_SEARCH_PLUGIN.NAME}
Expand All @@ -72,6 +83,23 @@ export const RoleMappings: React.FC = () => {
</section>
);

const usersTable = (
<UsersTable
accessItemKey="engines"
singleUserRoleMappings={singleUserRoleMappings}
initializeSingleUserRoleMapping={initializeSingleUserRoleMapping}
handleDeleteMapping={handleDeleteMapping}
/>
);

const usersSection = (
<>
<UsersHeading onClick={() => initializeSingleUserRoleMapping()} />
<EuiSpacer />
{hasUsers ? usersTable : <UsersEmptyPrompt />}
</>
);

return (
<AppSearchPageTemplate
pageChrome={[ROLE_MAPPINGS_TITLE]}
Expand All @@ -81,7 +109,10 @@ export const RoleMappings: React.FC = () => {
emptyState={rolesEmptyState}
>
{roleMappingFlyoutOpen && <RoleMapping />}
{singleUserRoleMappingFlyoutOpen && <User />}
{roleMappingsSection}
<EuiSpacer size="xxl" />
{usersSection}
</AppSearchPageTemplate>
);
};
Loading

0 comments on commit 4b546bf

Please sign in to comment.