Files
grafana/pkg/services/provisioning/notifiers/config_reader.go
T
Joan López de la Franca Beltran 722c414fef Encryption: Refactor securejsondata.SecureJsonData to stop relying on global functions (#38865)
* Encryption: Add support to encrypt/decrypt sjd

* Add datasources.Service as a proxy to datasources db operations

* Encrypt ds.SecureJsonData before calling SQLStore

* Move ds cache code into ds service

* Fix tlsmanager tests

* Fix pluginproxy tests

* Remove some securejsondata.GetEncryptedJsonData usages

* Add pluginsettings.Service as a proxy for plugin settings db operations

* Add AlertNotificationService as a proxy for alert notification db operations

* Remove some securejsondata.GetEncryptedJsonData usages

* Remove more securejsondata.GetEncryptedJsonData usages

* Fix lint errors

* Minor fixes

* Remove encryption global functions usages from ngalert

* Fix lint errors

* Minor fixes

* Minor fixes

* Remove securejsondata.DecryptedValue usage

* Refactor the refactor

* Remove securejsondata.DecryptedValue usage

* Move securejsondata to migrations package

* Move securejsondata to migrations package

* Minor fix

* Fix integration test

* Fix integration tests

* Undo undesired changes

* Fix tests

* Add context.Context into encryption methods

* Fix tests

* Fix tests

* Fix tests

* Trigger CI

* Fix test

* Add names to params of encryption service interface

* Remove bus from CacheServiceImpl

* Add logging

* Add keys to logger

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>

* Add missing key to logger

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>

* Undo changes in markdown files

* Fix formatting

* Add context to secrets service

* Rename decryptSecureJsonData to decryptSecureJsonDataFn

* Name args in GetDecryptedValueFn

* Add template back to NewAlertmanagerNotifier

* Copy GetDecryptedValueFn to ngalert

* Add logging to pluginsettings

* Fix pluginsettings test

Co-authored-by: Tania B <yalyna.ts@gmail.com>
Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
2021-10-07 17:33:50 +03:00

188 lines
4.9 KiB
Go

package notifiers
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/encryption"
"github.com/grafana/grafana/pkg/services/provisioning/utils"
"github.com/grafana/grafana/pkg/setting"
"gopkg.in/yaml.v2"
)
type configReader struct {
encryptionService encryption.Service
log log.Logger
}
func (cr *configReader) readConfig(path string) ([]*notificationsAsConfig, error) {
var notifications []*notificationsAsConfig
cr.log.Debug("Looking for alert notification provisioning files", "path", path)
files, err := ioutil.ReadDir(path)
if err != nil {
cr.log.Error("Can't read alert notification provisioning files from directory", "path", path, "error", err)
return notifications, nil
}
for _, file := range files {
if strings.HasSuffix(file.Name(), ".yaml") || strings.HasSuffix(file.Name(), ".yml") {
cr.log.Debug("Parsing alert notifications provisioning file", "path", path, "file.Name", file.Name())
notifs, err := cr.parseNotificationConfig(path, file)
if err != nil {
return nil, err
}
if notifs != nil {
notifications = append(notifications, notifs)
}
}
}
cr.log.Debug("Validating alert notifications")
if err = cr.validateRequiredField(notifications); err != nil {
return nil, err
}
if err := cr.checkOrgIDAndOrgName(notifications); err != nil {
return nil, err
}
if err := cr.validateNotifications(notifications); err != nil {
return nil, err
}
return notifications, nil
}
func (cr *configReader) parseNotificationConfig(path string, file os.FileInfo) (*notificationsAsConfig, error) {
filename, _ := filepath.Abs(filepath.Join(path, file.Name()))
// nolint:gosec
// We can ignore the gosec G304 warning on this one because `filename` comes from ps.Cfg.ProvisioningPath
yamlFile, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var cfg *notificationsAsConfigV0
err = yaml.Unmarshal(yamlFile, &cfg)
if err != nil {
return nil, err
}
return cfg.mapToNotificationFromConfig(), nil
}
func (cr *configReader) checkOrgIDAndOrgName(notifications []*notificationsAsConfig) error {
for i := range notifications {
for _, notification := range notifications[i].Notifications {
if notification.OrgID < 1 {
if notification.OrgName == "" {
notification.OrgID = 1
} else {
notification.OrgID = 0
}
} else {
if err := utils.CheckOrgExists(notification.OrgID); err != nil {
return fmt.Errorf("failed to provision %q notification: %w", notification.Name, err)
}
}
}
for _, notification := range notifications[i].DeleteNotifications {
if notification.OrgID < 1 {
if notification.OrgName == "" {
notification.OrgID = 1
} else {
notification.OrgID = 0
}
}
}
}
return nil
}
func (cr *configReader) validateRequiredField(notifications []*notificationsAsConfig) error {
for i := range notifications {
var errStrings []string
for index, notification := range notifications[i].Notifications {
if notification.Name == "" {
errStrings = append(
errStrings,
fmt.Sprintf("Added alert notification item %d in configuration doesn't contain required field name", index+1),
)
}
if notification.UID == "" {
errStrings = append(
errStrings,
fmt.Sprintf("Added alert notification item %d in configuration doesn't contain required field uid", index+1),
)
}
}
for index, notification := range notifications[i].DeleteNotifications {
if notification.Name == "" {
errStrings = append(
errStrings,
fmt.Sprintf("Deleted alert notification item %d in configuration doesn't contain required field name", index+1),
)
}
if notification.UID == "" {
errStrings = append(
errStrings,
fmt.Sprintf("Deleted alert notification item %d in configuration doesn't contain required field uid", index+1),
)
}
}
if len(errStrings) != 0 {
return fmt.Errorf(strings.Join(errStrings, "\n"))
}
}
return nil
}
func (cr *configReader) validateNotifications(notifications []*notificationsAsConfig) error {
for i := range notifications {
if notifications[i].Notifications == nil {
continue
}
for _, notification := range notifications[i].Notifications {
encryptedSecureSettings, err := cr.encryptionService.EncryptJsonData(
context.Background(),
notification.SecureSettings,
setting.SecretKey,
)
if err != nil {
return err
}
_, err = alerting.InitNotifier(&models.AlertNotification{
Name: notification.Name,
Settings: notification.SettingsToJSON(),
SecureSettings: encryptedSecureSettings,
Type: notification.Type,
}, cr.encryptionService.GetDecryptedValue)
if err != nil {
return err
}
}
}
return nil
}