Alerting: Fix contact points scoped permission check (#108851)
* Fix scoped contact point permission check * Use annotations permissions for k8s API and globals permissions for external AMs * Fix scoped permission check for GrafanaReceiverForm
This commit is contained in:
+2
-2
@@ -68,14 +68,14 @@ export const ContactPointHeader = ({ contactPoint, onDelete }: ContactPointHeade
|
||||
*/
|
||||
const isReferencedByAnything = usingK8sApi ? Boolean(numberOfPolicies || numberOfRules) : policies.length > 0;
|
||||
/** Does the current user have permissions to edit the contact point? */
|
||||
const hasAbilityToEdit = canEditEntity(contactPoint) || editAllowed;
|
||||
const hasAbilityToEdit = usingK8sApi ? canEditEntity(contactPoint) : editAllowed;
|
||||
/** Can the contact point actually be edited via the UI? */
|
||||
const contactPointIsEditable = !provisioned;
|
||||
/** Given the alertmanager, the user's permissions, and the state of the contact point - can it actually be edited? */
|
||||
const canEdit = editSupported && hasAbilityToEdit && contactPointIsEditable;
|
||||
|
||||
/** Does the current user have permissions to delete the contact point? */
|
||||
const hasAbilityToDelete = canDeleteEntity(contactPoint) || deleteAllowed;
|
||||
const hasAbilityToDelete = usingK8sApi ? canDeleteEntity(contactPoint) : deleteAllowed;
|
||||
/** Can the contact point actually be deleted, regardless of permissions? i.e. ensuring it isn't provisioned and isn't referenced elsewhere */
|
||||
const contactPointIsDeleteable = !provisioned && !numberOfPoliciesPreventingDeletion && !numberOfRules;
|
||||
/** Given the alertmanager, the user's permissions, and the state of the contact point - can it actually be deleted? */
|
||||
|
||||
+48
-12
@@ -198,12 +198,37 @@ describe('contact points', () => {
|
||||
const unusedBadge = screen.getAllByLabelText('unused');
|
||||
expect(unusedBadge).toHaveLength(4);
|
||||
|
||||
const viewProvisioned = screen.getByTestId('view-action');
|
||||
expect(viewProvisioned).toBeInTheDocument();
|
||||
expect(viewProvisioned).toBeEnabled();
|
||||
// Two contact points should have view buttons: grafana-default-email (cannot be edited) and provisioned-contact-point (provisioned)
|
||||
const viewButtons = screen.getAllByRole('link', { name: /^view$/i });
|
||||
expect(viewButtons).toHaveLength(2);
|
||||
|
||||
// Check view buttons by their href to verify which contact points they belong to
|
||||
// The url is the same but the form should be readonly
|
||||
expect(viewButtons[0]).toHaveAttribute('href', '/alerting/notifications/receivers/grafana-default-email/edit');
|
||||
expect(viewButtons[1]).toHaveAttribute(
|
||||
'href',
|
||||
'/alerting/notifications/receivers/provisioned-contact-point/edit'
|
||||
);
|
||||
|
||||
viewButtons.forEach((button) => {
|
||||
expect(button).toBeEnabled();
|
||||
});
|
||||
|
||||
// Three contact points should have edit buttons: lotsa-emails, Slack with multiple channels, OnCall Contact point
|
||||
const editButtons = screen.getAllByRole('link', { name: /^edit$/i });
|
||||
expect(editButtons).toHaveLength(3);
|
||||
|
||||
// Check edit buttons by their href to verify which contact points they belong to
|
||||
expect(editButtons[0]).toHaveAttribute('href', '/alerting/notifications/receivers/lotsa-emails/edit');
|
||||
expect(editButtons[1]).toHaveAttribute(
|
||||
'href',
|
||||
'/alerting/notifications/receivers/OnCall%20Conctact%20point/edit'
|
||||
);
|
||||
expect(editButtons[2]).toHaveAttribute(
|
||||
'href',
|
||||
'/alerting/notifications/receivers/Slack%20with%20multiple%20channels/edit'
|
||||
);
|
||||
|
||||
const editButtons = screen.getAllByTestId('edit-action');
|
||||
expect(editButtons).toHaveLength(4);
|
||||
editButtons.forEach((button) => {
|
||||
expect(button).toBeEnabled();
|
||||
});
|
||||
@@ -227,11 +252,11 @@ describe('contact points', () => {
|
||||
expect(screen.getByRole('link', { name: 'add contact point' })).toHaveAttribute('aria-disabled', 'true');
|
||||
|
||||
// edit permission is based on API response - we should have 3 buttons
|
||||
const editButtons = await screen.findAllByTestId('edit-action');
|
||||
const editButtons = await screen.findAllByRole('link', { name: /^edit$/i });
|
||||
expect(editButtons).toHaveLength(3);
|
||||
|
||||
// there should be view buttons though - one for provisioned, and one for the un-editable contact point
|
||||
const viewButtons = screen.getAllByTestId('view-action');
|
||||
const viewButtons = screen.getAllByRole('link', { name: /^view$/i });
|
||||
expect(viewButtons).toHaveLength(2);
|
||||
|
||||
// check buttons in Notification Templates
|
||||
@@ -329,7 +354,18 @@ describe('contact points', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const { user } = renderWithProvider(<ContactPoint contactPoint={{ ...basicContactPoint, policies }} />);
|
||||
// Add the necessary K8s annotations to allow deletion
|
||||
const contactPointWithDeletePermission: ContactPointWithMetadata = {
|
||||
...basicContactPoint,
|
||||
metadata: {
|
||||
annotations: {
|
||||
[K8sAnnotations.AccessDelete]: 'true',
|
||||
},
|
||||
},
|
||||
policies,
|
||||
};
|
||||
|
||||
const { user } = renderWithProvider(<ContactPoint contactPoint={contactPointWithDeletePermission} />);
|
||||
|
||||
const moreActions = screen.getByRole('button', { name: /More/ });
|
||||
await user.click(moreActions);
|
||||
@@ -387,7 +423,7 @@ describe('contact points', () => {
|
||||
const unusedBadge = screen.getAllByLabelText('unused');
|
||||
expect(unusedBadge).toHaveLength(1);
|
||||
|
||||
const editButtons = screen.getAllByTestId('edit-action');
|
||||
const editButtons = screen.getAllByRole('link', { name: /^edit$/i });
|
||||
expect(editButtons).toHaveLength(2);
|
||||
editButtons.forEach((button) => {
|
||||
expect(button).toBeEnabled();
|
||||
@@ -431,9 +467,9 @@ describe('contact points', () => {
|
||||
|
||||
expect(screen.queryByRole('link', { name: 'add contact point' })).not.toBeInTheDocument();
|
||||
|
||||
const viewProvisioned = screen.getByTestId('view-action');
|
||||
expect(viewProvisioned).toBeInTheDocument();
|
||||
expect(viewProvisioned).toBeEnabled();
|
||||
const viewButton = screen.getByRole('link', { name: /^view$/i });
|
||||
expect(viewButton).toBeInTheDocument();
|
||||
expect(viewButton).toBeEnabled();
|
||||
|
||||
// check buttons in Notification Templates
|
||||
const notificationTemplatesTab = screen.getByRole('tab', { name: 'Notification Templates' });
|
||||
|
||||
+2
-1
@@ -44,7 +44,8 @@ beforeEach(() => {
|
||||
grantUserPermissions([AccessControlAction.AlertingNotificationsRead, AccessControlAction.AlertingNotificationsWrite]);
|
||||
});
|
||||
|
||||
const getTemplatePreviewContent = async () => within(screen.getByTestId('template-preview')).findByTestId('mockeditor');
|
||||
const getTemplatePreviewContent = async () =>
|
||||
within(await screen.findByTestId('template-preview')).findByTestId('mockeditor');
|
||||
|
||||
const templatesSelectorTestId = 'existing-templates-selector';
|
||||
|
||||
|
||||
+3
-3
@@ -135,9 +135,9 @@ export const GrafanaReceiverForm = ({ contactPoint, readOnly = false, editMode }
|
||||
}
|
||||
};
|
||||
|
||||
const isEditable = Boolean(
|
||||
(!readOnly || (contactPoint && canEditEntity(contactPoint))) && !contactPoint?.provisioned
|
||||
);
|
||||
// If there is no contact point it means we're creating a new one, so scoped permissions doesn't exist yet
|
||||
const hasScopedEditPermissions = contactPoint ? canEditEntity(contactPoint) : true;
|
||||
const isEditable = !readOnly && hasScopedEditPermissions && !contactPoint?.provisioned;
|
||||
const isTestable = !readOnly;
|
||||
|
||||
if (isLoadingNotifiers || isLoadingOnCallIntegration) {
|
||||
|
||||
Reference in New Issue
Block a user