Alerting: Update Discord settings to treat 'url' as a secure setting (#69588)

* make discord url secure

* support migrating unsecure settings to secure settings

* Update public/app/features/alerting/unified/utils/receiver-form.ts

Co-authored-by: William Wernert <william.wernert@grafana.com>

---------

Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
Co-authored-by: William Wernert <william.wernert@grafana.com>
This commit is contained in:
Yuri Tseretyan
2023-08-16 03:03:56 -04:00
committed by GitHub
parent 870ef7b058
commit 90e3f516ff
6 changed files with 104 additions and 8 deletions

View File

@@ -997,6 +997,7 @@ func GetAvailableNotifiers() []*NotifierPlugin {
Placeholder: "Discord webhook URL",
PropertyName: "url",
Required: true,
Secure: true,
},
{
Label: "Avatar URL",

View File

@@ -68,6 +68,7 @@ export function ChannelOptions<R extends ChannelValues>({
)?.[option.propertyName];
const defaultValue = defaultValues?.settings?.[option.propertyName];
const hasSecureProperty = defaultValues.secureSettings?.[option.propertyName];
return (
<OptionField
@@ -76,7 +77,7 @@ export function ChannelOptions<R extends ChannelValues>({
key={key}
error={error}
pathPrefix={pathPrefix}
pathSuffix={option.secure ? 'secureSettings.' : 'settings.'}
pathSuffix={option.secure && hasSecureProperty ? 'secureSettings.' : 'settings.'}
option={option}
/>
);

View File

@@ -68,7 +68,9 @@ export const GrafanaReceiverForm = ({ existing, alertManagerSourceName, config }
}, [existing, grafanaNotifiers.result]);
const onSubmit = (values: ReceiverFormValues<GrafanaChannelValues>) => {
const newReceiver = formValuesToGrafanaReceiver(values, id2original, defaultChannelValues);
const notifiers = grafanaNotifiers.result;
const newReceiver = formValuesToGrafanaReceiver(values, id2original, defaultChannelValues, notifiers ?? []);
dispatch(
updateAlertManagerConfigAction({
newConfig: updateConfigWithReceiver(config, newReceiver, existing?.name),

View File

@@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`formValuesToGrafanaReceiver should migrate regular settings to secure settings if the field is defined as secure 1`] = `
{
"grafana_managed_receiver_configs": [
{
"disableResolveMessage": false,
"name": "my-receiver",
"secureSettings": {
"url": "https://foo.bar/",
},
"settings": {},
"type": "discord",
"uid": "abc123",
},
],
"name": "my-receiver",
}
`;

View File

@@ -1,4 +1,8 @@
import { omitEmptyValues, omitEmptyUnlessExisting } from './receiver-form';
import { NotifierDTO } from 'app/types';
import { GrafanaChannelValues, ReceiverFormValues } from '../types/receiver-form';
import { formValuesToGrafanaReceiver, omitEmptyValues, omitEmptyUnlessExisting } from './receiver-form';
describe('Receiver form utils', () => {
describe('omitEmptyStringValues', () => {
@@ -64,3 +68,46 @@ describe('Receiver form utils', () => {
});
});
});
describe('formValuesToGrafanaReceiver', () => {
it('should migrate regular settings to secure settings if the field is defined as secure', () => {
const formValues: ReceiverFormValues<GrafanaChannelValues> = {
name: 'my-receiver',
items: [
{
__id: '1',
secureSettings: {},
secureFields: {},
type: 'discord',
settings: {
url: 'https://foo.bar/',
},
disableResolveMessage: false,
},
],
};
const channelMap = {
'1': {
uid: 'abc123',
secureSettings: {},
secureFields: {},
type: 'discord',
settings: {
url: 'https://foo.bar/',
},
disableResolveMessage: false,
},
};
const notifiers = [
{
type: 'discord',
options: [{ propertyName: 'url', secure: true }],
},
] as NotifierDTO[];
// @ts-expect-error
expect(formValuesToGrafanaReceiver(formValues, channelMap, {}, notifiers)).toMatchSnapshot();
});
});

View File

@@ -1,4 +1,4 @@
import { isArray, isNil, omitBy } from 'lodash';
import { isArray, omit, pick, isNil, omitBy } from 'lodash';
import {
AlertManagerCortexConfig,
@@ -79,13 +79,22 @@ export function cloudReceiverToFormValues(
export function formValuesToGrafanaReceiver(
values: ReceiverFormValues<GrafanaChannelValues>,
channelMap: GrafanaChannelMap,
defaultChannelValues: GrafanaChannelValues
defaultChannelValues: GrafanaChannelValues,
notifiers: NotifierDTO[]
): Receiver {
return {
name: values.name,
grafana_managed_receiver_configs: (values.items ?? []).map((channelValues) => {
const existing: GrafanaManagedReceiverConfig | undefined = channelMap[channelValues.__id];
return formChannelValuesToGrafanaChannelConfig(channelValues, defaultChannelValues, values.name, existing);
const notifier = notifiers.find((notifier) => notifier.type === channelValues.type);
return formChannelValuesToGrafanaChannelConfig(
channelValues,
defaultChannelValues,
values.name,
existing,
notifier
);
}),
};
}
@@ -202,7 +211,7 @@ function grafanaChannelConfigToFormChannelValues(
// work around https://github.com/grafana/alerting-squad/issues/100
notifier?.options.forEach((option) => {
if (option.secure && values.settings[option.propertyName]) {
if (option.secure && values.secureSettings[option.propertyName]) {
delete values.settings[option.propertyName];
values.secureFields[option.propertyName] = true;
}
@@ -215,7 +224,8 @@ export function formChannelValuesToGrafanaChannelConfig(
values: GrafanaChannelValues,
defaults: GrafanaChannelValues,
name: string,
existing?: GrafanaManagedReceiverConfig
existing?: GrafanaManagedReceiverConfig,
notifier?: NotifierDTO
): GrafanaManagedReceiverConfig {
const channel: GrafanaManagedReceiverConfig = {
settings: omitEmptyValues({
@@ -228,9 +238,25 @@ export function formChannelValuesToGrafanaChannelConfig(
disableResolveMessage:
values.disableResolveMessage ?? existing?.disableResolveMessage ?? defaults.disableResolveMessage,
};
// find all secure field definitions
const secureFieldNames: string[] =
notifier?.options.filter((option) => option.secure).map((option) => option.propertyName) ?? [];
// we make sure all fields that are marked as "secure" will be moved to "SecureSettings" instead of "settings"
const shouldBeSecure = pick(channel.settings, secureFieldNames);
channel.secureSettings = {
...shouldBeSecure,
...channel.secureSettings,
};
// remove the secure ones from the regular settings
channel.settings = omit(channel.settings, secureFieldNames);
if (existing) {
channel.uid = existing.uid;
}
return channel;
}