Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
5 changes: 5 additions & 0 deletions x-pack/plugins/security/public/management/management_urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ export const EDIT_ROLE_MAPPING_PATH = `/edit`;

export const getEditRoleMappingHref = (roleMappingName: string) =>
`${EDIT_ROLE_MAPPING_PATH}/${encodeURIComponent(roleMappingName)}`;

export const CLONE_ROLE_MAPPING_PATH = `/clone`;

export const getCloneRoleMappingHref = (roleMappingName: string) =>
`${CLONE_ROLE_MAPPING_PATH}/${encodeURIComponent(roleMappingName)}`;
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ interface Props {

export type DeleteRoleMappings = (
roleMappings: RoleMapping[],
onSuccess?: OnSuccessCallback
onSuccess?: OnSuccessCallback,
onCancel?: OnCancelCallback
) => void;

type OnSuccessCallback = (deletedRoleMappings: string[]) => void;
type OnCancelCallback = () => void;

export const DeleteProvider: React.FunctionComponent<Props> = ({
roleMappingsAPI,
Expand All @@ -39,24 +41,34 @@ export const DeleteProvider: React.FunctionComponent<Props> = ({
const [isDeleteInProgress, setIsDeleteInProgress] = useState(false);

const onSuccessCallback = useRef<OnSuccessCallback | null>(null);
const onCancelCallback = useRef<OnCancelCallback | null>(null);

const deleteRoleMappingsPrompt: DeleteRoleMappings = (
roleMappingsToDelete,
onSuccess = () => undefined
onSuccess = () => undefined,
onCancel = () => undefined
) => {
if (!roleMappingsToDelete || !roleMappingsToDelete.length) {
throw new Error('No Role Mappings specified for delete');
}
setIsModalOpen(true);
setRoleMappings(roleMappingsToDelete);
onSuccessCallback.current = onSuccess;
onCancelCallback.current = onCancel;
};

const closeModal = () => {
setIsModalOpen(false);
setRoleMappings([]);
};

const handleCancelModel = () => {
closeModal();
if (onCancelCallback.current) {
onCancelCallback.current();
}
};

const deleteRoleMappings = async () => {
let result;

Expand Down Expand Up @@ -161,7 +173,7 @@ export const DeleteProvider: React.FunctionComponent<Props> = ({
}
)
}
onCancel={closeModal}
onCancel={handleCancelModel}
onConfirm={deleteRoleMappings}
cancelButtonText={i18n.translate(
'xpack.security.management.roleMappings.deleteRoleMapping.confirmModal.cancelButtonLabel',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('EditRoleMappingPage', () => {
return mountWithIntl(
<KibanaContextProvider services={coreStart}>
<EditRoleMappingPage
action="edit"
name={name}
roleMappingsAPI={roleMappingsAPI}
rolesAPIClient={rolesAPI}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ interface State {
}

interface Props {
action: 'edit' | 'clone';
name?: string;
roleMappingsAPI: PublicMethodsOf<RoleMappingsAPIClient>;
rolesAPIClient: PublicMethodsOf<RolesAPIClient>;
Expand Down Expand Up @@ -295,13 +296,17 @@ export class EditRoleMappingPage extends Component<Props, State> {
});
};

private editingExistingRoleMapping = () => typeof this.props.name === 'string';
private editingExistingRoleMapping = () =>
typeof this.props.name === 'string' && this.props.action === 'edit';

private cloningExistingRoleMapping = () =>
typeof this.props.name === 'string' && this.props.action === 'clone';

private async loadAppData() {
try {
const [features, roleMapping] = await Promise.all([
this.props.roleMappingsAPI.checkRoleMappingFeatures(),
this.editingExistingRoleMapping()
this.editingExistingRoleMapping() || this.cloningExistingRoleMapping()
? this.props.roleMappingsAPI.getRoleMapping(this.props.name!)
: Promise.resolve({
name: '',
Expand All @@ -327,7 +332,10 @@ export class EditRoleMappingPage extends Component<Props, State> {
hasCompatibleRealms,
canUseStoredScripts,
canUseInlineScripts,
roleMapping,
roleMapping: {
...roleMapping,
name: this.cloningExistingRoleMapping() ? '' : roleMapping.name,
},
});
} catch (e) {
this.props.notifications.toasts.addDanger({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { act } from '@testing-library/react';
import React from 'react';

import { findTestSubject, mountWithIntl, nextTick } from '@kbn/test/jest';
import type { CoreStart, ScopedHistory } from 'src/core/public';
import type { CoreStart } from 'src/core/public';
import { coreMock, scopedHistoryMock } from 'src/core/public/mocks';
import { KibanaContextProvider } from 'src/plugins/kibana_react/public';

Expand All @@ -21,7 +21,7 @@ import { EmptyPrompt } from './empty_prompt';
import { RoleMappingsGridPage } from './role_mappings_grid_page';

describe('RoleMappingsGridPage', () => {
let history: ScopedHistory;
let history: ReturnType<typeof scopedHistoryMock.create>;
let coreStart: CoreStart;

const renderView = (
Expand All @@ -44,6 +44,7 @@ describe('RoleMappingsGridPage', () => {

beforeEach(() => {
history = scopedHistoryMock.create();
history.createHref.mockImplementation((location) => location.pathname!);
coreStart = coreMock.createStart();
});

Expand Down Expand Up @@ -188,6 +189,7 @@ describe('RoleMappingsGridPage', () => {
expect(roleMappingsAPI.getRoleMappings).toHaveBeenCalledTimes(1);
expect(roleMappingsAPI.deleteRoleMappings).not.toHaveBeenCalled();

findTestSubject(wrapper, `euiCollapsedItemActionsButton`).simulate('click');
findTestSubject(wrapper, `deleteRoleMappingButton-some-realm`).simulate('click');
expect(findTestSubject(wrapper, 'deleteRoleMappingConfirmationModal')).toHaveLength(1);

Expand Down Expand Up @@ -246,4 +248,55 @@ describe('RoleMappingsGridPage', () => {
`"The kibana_user role is deprecated. I don't like you."`
);
});

it('renders role mapping actions as appropriate', async () => {
const roleMappingsAPI = roleMappingsAPIClientMock.create();
roleMappingsAPI.getRoleMappings.mockResolvedValue([
{
name: 'some-realm',
enabled: true,
roles: ['superuser'],
rules: { field: { username: '*' } },
},
]);
roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({
canManageRoleMappings: true,
hasCompatibleRealms: true,
});
roleMappingsAPI.deleteRoleMappings.mockResolvedValue([
{
name: 'some-realm',
success: true,
},
]);

const wrapper = renderView(roleMappingsAPI);
await nextTick();
wrapper.update();

const editButton = wrapper.find(
'EuiButtonEmpty[data-test-subj="editRoleMappingButton-some-realm"]'
);
expect(editButton).toHaveLength(1);
expect(editButton.prop('href')).toBe('/edit/some-realm');

const cloneButton = wrapper.find(
'EuiButtonEmpty[data-test-subj="cloneRoleMappingButton-some-realm"]'
);
expect(cloneButton).toHaveLength(1);
expect(cloneButton.prop('href')).toBe('/clone/some-realm');

const actionMenuButton = wrapper.find(
'EuiButtonIcon[data-test-subj="euiCollapsedItemActionsButton"]'
);
expect(actionMenuButton).toHaveLength(1);

actionMenuButton.simulate('click');
wrapper.update();

const deleteButton = wrapper.find(
'EuiButtonEmpty[data-test-subj="deleteRoleMappingButton-some-realm"]'
);
expect(deleteButton).toHaveLength(1);
});
});
Loading