grafana/pkg/services/secrets/database/database.go
2022-07-14 23:53:43 +02:00

196 lines
4.9 KiB
Go

package database
import (
"context"
"fmt"
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/kmsproviders"
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/sqlstore"
"xorm.io/xorm"
)
const dataKeysTable = "data_keys"
type SecretsStoreImpl struct {
sqlStore *sqlstore.SQLStore
log log.Logger
}
func ProvideSecretsStore(sqlStore *sqlstore.SQLStore) *SecretsStoreImpl {
return &SecretsStoreImpl{
sqlStore: sqlStore,
log: log.New("secrets.store"),
}
}
func (ss *SecretsStoreImpl) GetDataKey(ctx context.Context, id string) (*secrets.DataKey, error) {
dataKey := &secrets.DataKey{}
var exists bool
err := ss.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
var err error
exists, err = sess.Table(dataKeysTable).
Where("name = ?", id).
Get(dataKey)
return err
})
if err != nil {
return nil, fmt.Errorf("failed getting data key: %w", err)
}
if !exists {
return nil, secrets.ErrDataKeyNotFound
}
return dataKey, nil
}
func (ss *SecretsStoreImpl) GetCurrentDataKey(ctx context.Context, label string) (*secrets.DataKey, error) {
dataKey := &secrets.DataKey{}
var exists bool
err := ss.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
var err error
exists, err = sess.Table(dataKeysTable).
Where("label = ? AND active = ?", label, ss.sqlStore.Dialect.BooleanStr(true)).
Get(dataKey)
return err
})
if !exists {
return nil, secrets.ErrDataKeyNotFound
}
if err != nil {
return nil, fmt.Errorf("failed getting current data key: %w", err)
}
return dataKey, nil
}
func (ss *SecretsStoreImpl) GetAllDataKeys(ctx context.Context) ([]*secrets.DataKey, error) {
result := make([]*secrets.DataKey, 0)
err := ss.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
err := sess.Table(dataKeysTable).Find(&result)
return err
})
return result, err
}
func (ss *SecretsStoreImpl) CreateDataKey(ctx context.Context, dataKey *secrets.DataKey) error {
return ss.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
return ss.CreateDataKeyWithDBSession(ctx, dataKey, sess.Session)
})
}
func (ss *SecretsStoreImpl) CreateDataKeyWithDBSession(_ context.Context, dataKey *secrets.DataKey, sess *xorm.Session) error {
if !dataKey.Active {
return fmt.Errorf("cannot insert deactivated data keys")
}
dataKey.Created = time.Now()
dataKey.Updated = dataKey.Created
_, err := sess.Table(dataKeysTable).Insert(dataKey)
return err
}
func (ss *SecretsStoreImpl) DisableDataKeys(ctx context.Context) error {
return ss.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
_, err := sess.Table(dataKeysTable).
Where("active = ?", ss.sqlStore.Dialect.BooleanStr(true)).
UseBool("active").Update(&secrets.DataKey{Active: false})
return err
})
}
func (ss *SecretsStoreImpl) DeleteDataKey(ctx context.Context, id string) error {
if len(id) == 0 {
return fmt.Errorf("data key id is missing")
}
return ss.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
_, err := sess.Table(dataKeysTable).Delete(&secrets.DataKey{Id: id})
return err
})
}
func (ss *SecretsStoreImpl) ReEncryptDataKeys(
ctx context.Context,
providers map[secrets.ProviderID]secrets.Provider,
currProvider secrets.ProviderID,
) error {
keys := make([]*secrets.DataKey, 0)
if err := ss.sqlStore.NewSession(ctx).Table(dataKeysTable).Find(&keys); err != nil {
return err
}
for _, k := range keys {
err := ss.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
provider, ok := providers[kmsproviders.NormalizeProviderID(k.Provider)]
if !ok {
ss.log.Warn(
"Could not find provider to re-encrypt data encryption key",
"id", k.Id,
"label", k.Label,
"provider", k.Provider,
)
return nil
}
decrypted, err := provider.Decrypt(ctx, k.EncryptedData)
if err != nil {
ss.log.Warn(
"Error while decrypting data encryption key to re-encrypt it",
"id", k.Id,
"label", k.Label,
"provider", k.Provider,
"err", err,
)
return nil
}
// Updating current data key by re-encrypting it with current provider.
// Accessing the current provider within providers map should be safe.
k.Provider = currProvider
k.Label = secrets.KeyLabel(k.Scope, currProvider)
k.Updated = time.Now()
k.EncryptedData, err = providers[currProvider].Encrypt(ctx, decrypted)
if err != nil {
ss.log.Warn(
"Error while re-encrypting data encryption key",
"id", k.Id,
"label", k.Label,
"provider", k.Provider,
"err", err,
)
return nil
}
if _, err := sess.Table(dataKeysTable).Where("name = ?", k.Id).Update(k); err != nil {
ss.log.Warn(
"Error while re-encrypting data encryption key",
"id", k.Id,
"label", k.Label,
"provider", k.Provider,
"err", err,
)
return nil
}
return nil
})
if err != nil {
return err
}
}
return nil
}