Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Workplace Search] Role Mappings to Kibana #93123

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
da54c6f
Add routes for role mapings
scottybollinger Feb 22, 2021
08d2ce6
Initial copy/paste
scottybollinger Feb 18, 2021
45e1e21
Update RoleMappingsRouter
scottybollinger Feb 18, 2021
102e471
Update RoleMappings
scottybollinger Feb 18, 2021
ca191b1
Update RoleMapping
scottybollinger Feb 18, 2021
7ddaceb
Fix path in index and add route helper
scottybollinger Feb 22, 2021
85678e1
Update paths in RoleMappingsLogic
scottybollinger Feb 18, 2021
969ec35
Remove history in favor of KibanaLogic.navigateToUrl
scottybollinger Feb 22, 2021
b07a2e0
Add Role type
scottybollinger Feb 22, 2021
b9450ec
Remove ID prop
scottybollinger Feb 22, 2021
d88a1db
Replace contextual flash messages with global
scottybollinger Feb 22, 2021
ac949ed
Replace Rails http with kibana http
scottybollinger Feb 22, 2021
c617848
Fix route path
scottybollinger Feb 22, 2021
154cf03
Add route and update global navigation
scottybollinger Feb 22, 2021
708b8e8
Add breadcrumb/page title
scottybollinger Feb 22, 2021
ebed050
Update flash messages in RoleMapping
scottybollinger Feb 24, 2021
30dfcb7
Use explicit AttributeName type instead of string
scottybollinger Feb 24, 2021
710ea6b
Add i18n
scottybollinger Feb 24, 2021
d1b2051
Fix type issue
scottybollinger Feb 25, 2021
d6aa01f
Add tests for components and router
scottybollinger Feb 25, 2021
fabacae
Add optional to interface
scottybollinger Mar 1, 2021
65bc917
Add tests for RoleMappingsLogic
scottybollinger Mar 1, 2021
9210060
Merge branch 'master' into scottybollinger/ws-role-mapping
kibanamachine Mar 1, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import React from 'react';
import { EuiSpacer } from '@elastic/eui';

import { WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants';
import { getWorkplaceSearchUrl } from '../../../shared/enterprise_search_url';
import { SideNav, SideNavLink } from '../../../shared/layout';
import { NAV } from '../../constants';
import {
Expand Down Expand Up @@ -43,9 +42,7 @@ export const WorkplaceSearchNav: React.FC<Props> = ({
<SideNavLink to={GROUPS_PATH} subNav={groupsSubNav}>
{NAV.GROUPS}
</SideNavLink>
<SideNavLink isExternal to={getWorkplaceSearchUrl(`#${ROLE_MAPPINGS_PATH}`)}>
{NAV.ROLE_MAPPINGS}
</SideNavLink>
<SideNavLink to={ROLE_MAPPINGS_PATH}>{NAV.ROLE_MAPPINGS}</SideNavLink>
<SideNavLink to={SECURITY_PATH}>{NAV.SECURITY}</SideNavLink>
<SideNavLink subNav={settingsSubNav} to={ORG_SETTINGS_PATH}>
{NAV.SETTINGS}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
SOURCES_PATH,
PERSONAL_SOURCES_PATH,
ORG_SETTINGS_PATH,
ROLE_MAPPINGS_PATH,
SECURITY_PATH,
} from './routes';
import { SourcesRouter } from './views/content_sources';
Expand All @@ -36,6 +37,7 @@ import { GroupsRouter } from './views/groups';
import { GroupSubNav } from './views/groups/components/group_sub_nav';
import { Overview } from './views/overview';
import { Overview as OverviewMVP } from './views/overview_mvp';
import { RoleMappingsRouter } from './views/role_mappings';
import { Security } from './views/security';
import { SettingsRouter } from './views/settings';
import { SettingsSubNav } from './views/settings/components/settings_sub_nav';
Expand Down Expand Up @@ -111,6 +113,11 @@ export const WorkplaceSearchConfigured: React.FC<InitialAppData> = (props) => {
<GroupsRouter />
</Layout>
</Route>
<Route path={ROLE_MAPPINGS_PATH}>
<Layout navigation={<WorkplaceSearchNav />} restrictWidth readOnlyMode={readOnlyMode}>
<RoleMappingsRouter />
</Layout>
</Route>
<Route path={SECURITY_PATH}>
<Layout navigation={<WorkplaceSearchNav />} restrictWidth readOnlyMode={readOnlyMode}>
<Security />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,4 @@ export const getReindexJobRoute = (
isOrganization: boolean
) =>
getSourcesPath(generatePath(REINDEX_JOB_PATH, { sourceId, activeReindexJobId }), isOrganization);
export const getRoleMappingPath = (roleId: string) => generatePath(ROLE_MAPPING_PATH, { roleId });
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export interface Meta {
page: MetaPage;
}

export type Role = 'admin' | 'user';

export interface Group {
id: string;
name: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* 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 { i18n } from '@kbn/i18n';

export const DELETE_ROLE_MAPPING_MESSAGE = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.deleteRoleMappingButtonMessage',
{
defaultMessage:
'Are you sure you want to permanently delete this mapping? This action is not reversible and some users might lose access.',
}
);

export const DEFAULT_GROUP_NAME = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.defaultGroupName',
{
defaultMessage: 'Default',
}
);

export const ADMIN_ROLE_TYPE_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.adminRoleTypeDescription',
{
defaultMessage:
'Admins have complete access to all organization-wide settings, including content source, group and user management functionality.',
}
);

export const USER_ROLE_TYPE_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.userRoleTypeDescription',
{
defaultMessage:
"Users' feature access is limited to search interfaces and personal settings management.",
}
);

export const ROLE_SELECTOR_DISABLED_TEXT = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.roleSelectorDisabledText',
{
defaultMessage:
'You need at least one admin role mapping before you can create a user role mapping.',
}
);

export const GROUP_ASSIGNMENT_TITLE = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.groupAssignmentTitle',
{
defaultMessage: 'Group assignment',
}
);

export const GROUP_ASSIGNMENT_INVALID_ERROR = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.groupAssignmentInvalidError',
{
defaultMessage: 'At least one assigned group is required.',
}
);

export const GROUP_ASSIGNMENT_ALL_GROUPS_LABEL = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.groupAssignmentAllGroupsLabel',
{
defaultMessage: 'Include in all groups, including future groups',
}
);

export const EMPTY_ROLE_MAPPINGS_TITLE = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.emptyRoleMappingsTitle',
{
defaultMessage: 'No role mappings yet',
}
);

export const EMPTY_ROLE_MAPPINGS_BODY = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.emptyRoleMappingsBody',
{
defaultMessage:
'New team members are assigned the admin role by default. An admin can access everything. Create a new role to override the default.',
}
);

export const ROLE_MAPPINGS_TABLE_HEADER = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.roleMappingsTableHeader',
{
defaultMessage: 'Group Access',
}
);

export const ROLE_MAPPINGS_TITLE = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.roleMappingsTitle',
{
defaultMessage: 'Users & roles',
}
);

export const ROLE_MAPPINGS_DESCRIPTION = i18n.translate(
'xpack.enterpriseSearch.workplaceSearch.roleMapping.roleMappingsDescription',
{
defaultMessage:
'Define role mappings for elasticsearch-native and elasticsearch-saml authentication.',
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export { RoleMappingsRouter } from './role_mappings_router';
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* 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__/shallow_useeffect.mock';
import { setMockActions, setMockValues } from '../../../__mocks__';

import React from 'react';

import { shallow } from 'enzyme';

import { EuiCheckbox } from '@elastic/eui';

import { Loading } from '../../../shared/loading';
import {
AttributeSelector,
DeleteMappingCallout,
RoleSelector,
} from '../../../shared/role_mapping';
import { wsRoleMapping } from '../../../shared/role_mapping/__mocks__/roles';

import { RoleMapping } from './role_mapping';

describe('RoleMapping', () => {
const initializeRoleMappings = jest.fn();
const initializeRoleMapping = jest.fn();
const handleSaveMapping = jest.fn();
const handleGroupSelectionChange = jest.fn();
const handleAllGroupsSelectionChange = jest.fn();
const handleAttributeValueChange = jest.fn();
const handleAttributeSelectorChange = jest.fn();
const handleDeleteMapping = jest.fn();
const handleRoleChange = jest.fn();
const handleAuthProviderChange = jest.fn();
const resetState = jest.fn();
const groups = [
{
name: 'Group 1',
id: 'g1',
},
{
name: 'Group 2',
id: 'g2',
},
];
const mockValues = {
attributes: [],
elasticsearchRoles: [],
dataLoading: false,
roleType: 'admin',
roleMappings: [wsRoleMapping],
attributeValue: '',
attributeName: 'username',
availableGroups: groups,
selectedGroups: new Set(),
includeInAllGroups: false,
availableAuthProviders: [],
multipleAuthProvidersConfig: true,
selectedAuthProviders: [],
};

beforeEach(() => {
setMockActions({
initializeRoleMappings,
initializeRoleMapping,
handleSaveMapping,
handleGroupSelectionChange,
handleAllGroupsSelectionChange,
handleAttributeValueChange,
handleAttributeSelectorChange,
handleDeleteMapping,
handleRoleChange,
handleAuthProviderChange,
resetState,
});
setMockValues(mockValues);
});

it('renders', () => {
const wrapper = shallow(<RoleMapping />);

expect(wrapper.find(AttributeSelector)).toHaveLength(1);
expect(wrapper.find(RoleSelector)).toHaveLength(2);
});

it('returns Loading when loading', () => {
setMockValues({ ...mockValues, dataLoading: true });
const wrapper = shallow(<RoleMapping />);

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

it('hides DeleteMappingCallout for new mapping', () => {
const wrapper = shallow(<RoleMapping isNew />);

expect(wrapper.find(DeleteMappingCallout)).toHaveLength(0);
});

it('handles group checkbox click', () => {
const wrapper = shallow(<RoleMapping />);
wrapper
.find(EuiCheckbox)
.first()
.simulate('change', { target: { checked: true } });

expect(handleGroupSelectionChange).toHaveBeenCalledWith(groups[0].id, true);
});

it('handles all groups checkbox click', () => {
const wrapper = shallow(<RoleMapping />);
wrapper
.find(EuiCheckbox)
.last()
.simulate('change', { target: { checked: true } });

expect(handleAllGroupsSelectionChange).toHaveBeenCalledWith(true);
});
});
Loading