|
|
|
@ -3,6 +3,7 @@ package api |
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"crypto/md5" |
|
|
|
|
"encoding/base64" |
|
|
|
|
"encoding/json" |
|
|
|
|
"net/http" |
|
|
|
|
"testing" |
|
|
|
@ -12,6 +13,7 @@ import ( |
|
|
|
|
"github.com/google/go-cmp/cmp/cmpopts" |
|
|
|
|
"github.com/prometheus/alertmanager/pkg/labels" |
|
|
|
|
"github.com/prometheus/client_golang/prometheus" |
|
|
|
|
"github.com/stretchr/testify/assert" |
|
|
|
|
"github.com/stretchr/testify/require" |
|
|
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol" |
|
|
|
@ -259,6 +261,118 @@ func TestAlertmanagerConfig(t *testing.T) { |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestGetAlertmanagerConfiguration_NewSecretField(t *testing.T) { |
|
|
|
|
// This test has the following goals:
|
|
|
|
|
// Given:
|
|
|
|
|
// - A saved notifier config with an existing secret field stored unencrypted in Settings.
|
|
|
|
|
// Ensure:
|
|
|
|
|
// - The secret field is not returned in plaintext.
|
|
|
|
|
// - The secret field is returned as a bool in SecureFields.
|
|
|
|
|
// - The secret field is correctly saved and encrypted in SecureSettings when saving the notifier config without changes.
|
|
|
|
|
// - The secret field is removed from Settings when saving the notifier config.
|
|
|
|
|
|
|
|
|
|
sut := createSut(t) |
|
|
|
|
orgId := int64(1) |
|
|
|
|
|
|
|
|
|
// This config has the secret field "integrationKey" stored incorrectly and unencrypted in Settings.
|
|
|
|
|
configs := map[int64]*ngmodels.AlertConfiguration{ |
|
|
|
|
1: { |
|
|
|
|
OrgID: orgId, |
|
|
|
|
AlertmanagerConfiguration: `{ |
|
|
|
|
"alertmanager_config": { |
|
|
|
|
"route": { |
|
|
|
|
"receiver": "configWithNewlySecretSetting" |
|
|
|
|
}, |
|
|
|
|
"receivers": [{ |
|
|
|
|
"name": "configWithNewlySecretSetting", |
|
|
|
|
"grafana_managed_receiver_configs": [{ |
|
|
|
|
"uid": "configWithNewlySecretSetting-uid", |
|
|
|
|
"name": "configWithNewlySecretSetting", |
|
|
|
|
"type": "pagerduty", |
|
|
|
|
"settings": {"integrationKey": "unencrypted secure secret"}, |
|
|
|
|
"secureSettings": {} |
|
|
|
|
}] |
|
|
|
|
}] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
`, |
|
|
|
|
CreatedAt: time.Now().Unix(), |
|
|
|
|
Default: false, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Store the config as-is in the database. Bypasses normal save route so it doesn't get pre-emptively fixed.
|
|
|
|
|
mam := createMultiOrgAlertmanager(t, configs) |
|
|
|
|
sut.mam = mam |
|
|
|
|
|
|
|
|
|
rc := createRequestCtxInOrg(orgId) |
|
|
|
|
res := sut.RouteGetAlertingConfig(rc) |
|
|
|
|
gettable := asGettableUserConfig(t, res) |
|
|
|
|
|
|
|
|
|
integration := gettable.GetGrafanaReceiverMap()["configWithNewlySecretSetting-uid"] |
|
|
|
|
require.NotNil(t, integration) |
|
|
|
|
|
|
|
|
|
var settings map[string]string |
|
|
|
|
err := json.Unmarshal(integration.Settings, &settings) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
// The secret field "integrationKey" should not be returned in plaintext.
|
|
|
|
|
assert.NotEqual(t, "unencrypted secure secret", settings["integrationKey"]) |
|
|
|
|
// Just in case let's look for the unencrypted value anywhere in the settings.
|
|
|
|
|
assert.NotContains(t, string(integration.Settings), "unencrypted") |
|
|
|
|
|
|
|
|
|
// The secret fields should be returned as a bool in SecureFields.
|
|
|
|
|
assert.True(t, integration.SecureFields["integrationKey"]) |
|
|
|
|
|
|
|
|
|
// Now we save the config without changes. This should encrypt the field "integrationKey" into SecureSettings and
|
|
|
|
|
// remove it from Settings.
|
|
|
|
|
|
|
|
|
|
// Simulates FE-API interaction, "integrationKey" is not sent in Settings as the caller.
|
|
|
|
|
// Instead, it leaves it out of "SecureSettings" to indicate the API should keep the existing value.
|
|
|
|
|
var postWithoutChanges = `{ |
|
|
|
|
"alertmanager_config": { |
|
|
|
|
"route": { |
|
|
|
|
"receiver": "configWithNewlySecretSetting" |
|
|
|
|
}, |
|
|
|
|
"receivers": [{ |
|
|
|
|
"name": "configWithNewlySecretSetting", |
|
|
|
|
"grafana_managed_receiver_configs": [{ |
|
|
|
|
"uid": "configWithNewlySecretSetting-uid", |
|
|
|
|
"name": "configWithNewlySecretSetting", |
|
|
|
|
"type": "pagerduty", |
|
|
|
|
"settings": {}, |
|
|
|
|
"secureSettings": {} |
|
|
|
|
}] |
|
|
|
|
}] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
` |
|
|
|
|
postable := createAmConfigRequest(t, postWithoutChanges) |
|
|
|
|
|
|
|
|
|
res = sut.RoutePostAlertingConfig(rc, postable) |
|
|
|
|
require.Equal(t, 202, res.Status()) |
|
|
|
|
|
|
|
|
|
// Check that the secret field "integrationKey" is now encrypted in SecureSettings.
|
|
|
|
|
savedConfig := &apimodels.PostableUserConfig{} |
|
|
|
|
err = json.Unmarshal([]byte(configs[orgId].AlertmanagerConfiguration), savedConfig) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
savedIntegration := savedConfig.GetGrafanaReceiverMap()["configWithNewlySecretSetting-uid"] |
|
|
|
|
require.NotNil(t, savedIntegration) |
|
|
|
|
|
|
|
|
|
// No longer in Settings.
|
|
|
|
|
assert.Equal(t, "{}", string(savedIntegration.Settings)) |
|
|
|
|
|
|
|
|
|
// Encrypted in SecureSettings.
|
|
|
|
|
secureSecret := savedIntegration.SecureSettings["integrationKey"] |
|
|
|
|
assert.NotEmpty(t, secureSecret) |
|
|
|
|
encryptedSecret, err := base64.StdEncoding.DecodeString(secureSecret) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
// No access to .Decrypt, but we can check that it's not the same as the unencrypted value.
|
|
|
|
|
assert.NotEqual(t, "unencrypted secure secret", string(encryptedSecret)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestAlertmanagerAutogenConfig(t *testing.T) { |
|
|
|
|
createSutForAutogen := func(t *testing.T) (AlertmanagerSrv, map[int64]*ngmodels.AlertConfiguration) { |
|
|
|
|
sut := createSut(t) |
|
|
|
|