Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -149,4 +149,20 @@ journey(`PrivateLocationsSettings`, async ({ page, params }) => {
await page.click('button:has-text("Delete location")');
await page.click('text=Create your first private location');
});

step('login with non super user', async () => {
await page.click('[data-test-subj="userMenuAvatar"]');
await page.click('text="Log out"');
await syntheticsApp.loginToKibana('viewer', 'changeme');
});

step('viewer user cannot add locations', async () => {
await syntheticsApp.navigateToSettings(false);
await page.click('text=Private Locations');
await page.waitForSelector(
`text="You're missing some Kibana privileges to manage private locations"`
);
const createLocationBtn = await page.getByRole('button', { name: 'Create location' });
expect(await createLocationBtn.getAttribute('disabled')).toEqual('');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
*/

import React, { ReactNode } from 'react';
import { EuiCallOut, EuiToolTip } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiCallOut, EuiToolTip, EuiCode } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

export const FleetPermissionsCallout = () => {
return (
<EuiCallOut title={NEED_PERMISSIONS} color="warning" iconType="help">
<p>{NEED_FLEET_READ_AGENT_POLICIES_PERMISSION}</p>
<EuiCallOut title={NEED_PERMISSIONS_PRIVATE_LOCATIONS} color="warning" iconType="help">
<p>{NEED_PRIVATE_LOCATIONS_PERMISSION}</p>
</EuiCallOut>
);
};
Expand Down Expand Up @@ -62,34 +63,40 @@ function getRestrictionReasonLabel(
: undefined;
}

export const NEED_PERMISSIONS = i18n.translate(
'xpack.synthetics.monitorManagement.needPermissions',
export const NEED_PERMISSIONS_PRIVATE_LOCATIONS = i18n.translate(
'xpack.synthetics.monitorManagement.privateLocations.needPermissions',
{
defaultMessage: 'Need permissions',
defaultMessage: "You're missing some Kibana privileges to manage private locations",
}
);

export const NEED_FLEET_READ_AGENT_POLICIES_PERMISSION = i18n.translate(
'xpack.synthetics.monitorManagement.needFleetReadAgentPoliciesPermission',
{
defaultMessage:
'You are not authorized to access Fleet. Fleet permissions are required to create new private locations.',
}
export const ALL = i18n.translate('xpack.synthetics.monitorManagement.priviledges.all', {
defaultMessage: 'All',
});

export const NEED_PRIVATE_LOCATIONS_PERMISSION = (
<FormattedMessage
id="xpack.synthetics.monitorManagement.privateLocations.needFleetPermission"
defaultMessage="You are not authorized to manage private locations. It requires the {all} Kibana privilege for both Fleet and Integrations."
values={{
all: <EuiCode>{`"${ALL}"`}</EuiCode>,
}}
/>
);

export const CANNOT_SAVE_INTEGRATION_LABEL = i18n.translate(
'xpack.synthetics.monitorManagement.cannotSaveIntegration',
{
defaultMessage:
'You are not authorized to update integrations. Integrations write permissions are required.',
'You are not authorized to manage private locations. It requires the "All" Kibana privilege for both Fleet and Integrations.',
}
);

const CANNOT_PERFORM_ACTION_FLEET = i18n.translate(
'xpack.synthetics.monitorManagement.noFleetPermission',
{
defaultMessage:
'You are not authorized to perform this action. Integrations write permissions are required.',
'You are not authorized to perform this action. It requires the "All" Kibana privilege for Integrations.',
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
EuiButton,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useFleetPermissions } from '../../../hooks/use_fleet_permissions';
import { useCanManagePrivateLocation } from '../../../hooks/use_fleet_permissions';
import { useFormWrapped } from '../../../../../hooks/use_form_wrapped';
import { PrivateLocation } from '../../../../../../common/runtime_types';
import { FleetPermissionsCallout } from '../../common/components/permissions';
Expand Down Expand Up @@ -54,7 +54,7 @@ export const AddLocationFlyout = ({

const { handleSubmit } = form;

const { canReadAgentPolicies } = useFleetPermissions();
const canManagePrivateLocation = useCanManagePrivateLocation();

const closeFlyout = () => {
setIsOpen(false);
Expand All @@ -69,9 +69,12 @@ export const AddLocationFlyout = ({
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
{!canReadAgentPolicies && <FleetPermissionsCallout />}
{!canManagePrivateLocation && <FleetPermissionsCallout />}

<LocationForm privateLocations={privateLocations} />
<LocationForm
privateLocations={privateLocations}
hasPermissions={canManagePrivateLocation}
/>
</EuiFlyoutBody>
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
import { useSyntheticsSettingsContext } from '../../../contexts';
import { LEARN_MORE, READ_DOCS } from './empty_locations';

export const AgentPolicyNeeded = () => {
export const AgentPolicyNeeded = ({ disabled }: { disabled: boolean }) => {
const { basePath } = useSyntheticsSettingsContext();

return (
Expand All @@ -20,7 +20,12 @@ export const AgentPolicyNeeded = () => {
title={<h2>{AGENT_POLICY_NEEDED}</h2>}
body={<p>{ADD_AGENT_POLICY_DESCRIPTION}</p>}
actions={
<EuiButton fill href={`${basePath}/app/fleet/policies?create`} color="primary">
<EuiButton
fill
href={`${basePath}/app/fleet/policies?create`}
color="primary"
isDisabled={disabled}
>
{CREATE_AGENT_POLICY}
</EuiButton>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React, { useState } from 'react';
import { EuiButtonIcon, EuiConfirmModal, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useSyntheticsSettingsContext } from '../../../contexts';
import { useFleetPermissions } from '../../../hooks';
import { useFleetPermissions, useCanManagePrivateLocation } from '../../../hooks';
import { CANNOT_SAVE_INTEGRATION_LABEL } from '../../common/components/permissions';

export const DeleteLocation = ({
Expand All @@ -30,6 +30,7 @@ export const DeleteLocation = ({

const { canSave } = useSyntheticsSettingsContext();
const { canSaveIntegrations } = useFleetPermissions();
const canManagePrivateLocation = useCanManagePrivateLocation();

const [isModalOpen, setIsModalOpen] = useState(false);

Expand Down Expand Up @@ -62,7 +63,9 @@ export const DeleteLocation = ({
return (
<>
{isModalOpen && deleteModal}
<EuiToolTip content={canDelete && canSaveIntegrations ? DELETE_LABEL : deleteDisabledReason}>
<EuiToolTip
content={canDelete && canManagePrivateLocation ? DELETE_LABEL : deleteDisabledReason}
>
<EuiButtonIcon
data-test-subj={`deleteLocation-${id}`}
isLoading={loading}
Expand All @@ -72,7 +75,7 @@ export const DeleteLocation = ({
onClick={() => {
setIsModalOpen(true);
}}
isDisabled={!canDelete || !canSave}
isDisabled={!canDelete || !canManagePrivateLocation || !canSave}
/>
</EuiToolTip>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ import { selectAgentPolicies } from '../../../state/private_locations';

export const LocationForm = ({
privateLocations,
hasPermissions,
}: {
onDiscard?: () => void;
privateLocations: PrivateLocation[];
hasPermissions: boolean;
}) => {
const { data } = useSelector(selectAgentPolicies);
const { control, register } = useFormContext<PrivateLocation>();
Expand All @@ -41,7 +43,7 @@ export const LocationForm = ({

return (
<>
{data?.items.length === 0 && <AgentPolicyNeeded />}
{data?.items.length === 0 && <AgentPolicyNeeded disabled={!hasPermissions} />}
<EuiForm component="form" noValidate>
<EuiFormRow
fullWidth
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { ViewLocationMonitors } from './view_location_monitors';
import { TableTitle } from '../../common/components/table_title';
import { TAGS_LABEL } from '../components/tags_field';
import { useSyntheticsSettingsContext } from '../../../contexts';
import { useFleetPermissions } from '../../../hooks';
import { useCanManagePrivateLocation } from '../../../hooks';
import { setAddingNewPrivateLocation } from '../../../state/private_locations';
import { PrivateLocationDocsLink, START_ADDING_LOCATIONS_DESCRIPTION } from './empty_locations';
import { PrivateLocation } from '../../../../../../common/runtime_types';
Expand Down Expand Up @@ -53,7 +53,7 @@ export const PrivateLocationsTable = ({
const { locationMonitors, loading } = useLocationMonitors();

const { canSave } = useSyntheticsSettingsContext();
const { canSaveIntegrations } = useFleetPermissions();
const canManagePrivateLocations = useCanManagePrivateLocation();

const tagsList = privateLocations.reduce((acc, item) => {
const tags = item.tags || [];
Expand Down Expand Up @@ -133,10 +133,10 @@ export const PrivateLocationsTable = ({
fill
data-test-subj={'addPrivateLocationButton'}
isLoading={loading}
disabled={!canSaveIntegrations || !canSave}
disabled={!canManagePrivateLocations || !canSave}
onClick={() => setIsAddingNew(true)}
iconType="plusInCircle"
title={!canSaveIntegrations ? CANNOT_SAVE_INTEGRATION_LABEL : undefined}
title={!canManagePrivateLocations ? CANNOT_SAVE_INTEGRATION_LABEL : undefined}
>
{ADD_LABEL}
</EuiButton>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const ManageEmptyState: FC<{
const { data: agentPolicies } = useSelector(selectAgentPolicies);

if (agentPolicies?.total === 0) {
return <AgentPolicyNeeded />;
return <AgentPolicyNeeded disabled={!hasFleetPermissions} />;
}

if (privateLocations.length === 0) {
Expand Down
Loading