mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 03:02:18 +08:00
SecretsManager: Add decrypt service (#107473)
* SecretsManager: Add decrypt service Co-authored-by: Dana Axinte <53751979+dana-axinte@users.noreply.github.com> * Missed space to sync files
This commit is contained in:
36
pkg/registry/apis/secret/decrypt/service.go
Normal file
36
pkg/registry/apis/secret/decrypt/service.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package decrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||||
|
"github.com/grafana/grafana/pkg/registry/apis/secret/service"
|
||||||
|
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OSSDecryptService struct {
|
||||||
|
decryptStore contracts.DecryptStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ service.DecryptService = &OSSDecryptService{}
|
||||||
|
|
||||||
|
func ProvideDecryptService(decryptStore contracts.DecryptStorage) *OSSDecryptService {
|
||||||
|
return &OSSDecryptService{
|
||||||
|
decryptStore: decryptStore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *OSSDecryptService) Decrypt(ctx context.Context, namespace string, names ...string) (map[string]service.DecryptResult, error) {
|
||||||
|
results := make(map[string]service.DecryptResult, len(names))
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
exposedSecureValue, err := d.decryptStore.Decrypt(ctx, xkube.Namespace(namespace), name)
|
||||||
|
if err != nil {
|
||||||
|
results[name] = service.NewDecryptResultErr(err)
|
||||||
|
} else {
|
||||||
|
results[name] = service.NewDecryptResultValue(&exposedSecureValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
101
pkg/registry/apis/secret/decrypt/service_test.go
Normal file
101
pkg/registry/apis/secret/decrypt/service_test.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package decrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
|
||||||
|
"github.com/grafana/grafana/pkg/registry/apis/secret/service"
|
||||||
|
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDecryptService(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
t.Run("when there are only errors from the storage, the service returns them in the map", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mockErr := errors.New("mock error")
|
||||||
|
mockStorage := &MockDecryptStorage{}
|
||||||
|
mockStorage.On("Decrypt", mock.Anything, mock.Anything, mock.Anything).Return(secretv0alpha1.ExposedSecureValue(""), mockErr)
|
||||||
|
decryptedValuesResp := map[string]service.DecryptResult{
|
||||||
|
"secure-value-1": service.NewDecryptResultErr(mockErr),
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptService := &OSSDecryptService{
|
||||||
|
decryptStore: mockStorage,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := decryptService.Decrypt(ctx, "default", "secure-value-1")
|
||||||
|
require.NotNil(t, resp)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, decryptedValuesResp, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("when there is no error from the storage, it returns a map of the decrypted values", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mockStorage := &MockDecryptStorage{}
|
||||||
|
// Set up the mock to return a different value for each name in the test
|
||||||
|
exposedSecureValue1 := secretv0alpha1.NewExposedSecureValue("value1")
|
||||||
|
exposedSecureValue2 := secretv0alpha1.NewExposedSecureValue("value2")
|
||||||
|
mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-1").
|
||||||
|
Return(exposedSecureValue1, nil)
|
||||||
|
mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-2").
|
||||||
|
Return(exposedSecureValue2, nil)
|
||||||
|
|
||||||
|
decryptedValuesResp := map[string]service.DecryptResult{
|
||||||
|
"secure-value-1": service.NewDecryptResultValue(&exposedSecureValue1),
|
||||||
|
"secure-value-2": service.NewDecryptResultValue(&exposedSecureValue2),
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptService := &OSSDecryptService{
|
||||||
|
decryptStore: mockStorage,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := decryptService.Decrypt(ctx, "default", "secure-value-1", "secure-value-2")
|
||||||
|
require.NotNil(t, resp)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, decryptedValuesResp, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("when there is an error from the storage, the service returns a map of errors and decrypted values", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mockErr := errors.New("mock error")
|
||||||
|
mockStorage := &MockDecryptStorage{}
|
||||||
|
exposedSecureValue := secretv0alpha1.NewExposedSecureValue("value")
|
||||||
|
mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-1").
|
||||||
|
Return(exposedSecureValue, nil)
|
||||||
|
mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-2").
|
||||||
|
Return(secretv0alpha1.ExposedSecureValue(""), mockErr)
|
||||||
|
|
||||||
|
decryptedValuesResp := map[string]service.DecryptResult{
|
||||||
|
"secure-value-1": service.NewDecryptResultValue(&exposedSecureValue),
|
||||||
|
"secure-value-2": service.NewDecryptResultErr(mockErr),
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptService := &OSSDecryptService{
|
||||||
|
decryptStore: mockStorage,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := decryptService.Decrypt(ctx, "default", "secure-value-1", "secure-value-2")
|
||||||
|
require.NotNil(t, resp)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, decryptedValuesResp, resp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockDecryptStorage struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockDecryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (secretv0alpha1.ExposedSecureValue, error) {
|
||||||
|
args := m.Called(ctx, namespace, name)
|
||||||
|
return args.Get(0).(secretv0alpha1.ExposedSecureValue), args.Error(1)
|
||||||
|
}
|
36
pkg/registry/apis/secret/service/decrypt.go
Normal file
36
pkg/registry/apis/secret/service/decrypt.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecryptResult is the (union) result of a decryption operation.
|
||||||
|
// It contains the decrypted `value` when the decryption succeeds, and the `err` when it fails.
|
||||||
|
// It is not possible to construct a `DecryptResult` where both `value` and `err` are set from another package.
|
||||||
|
type DecryptResult struct {
|
||||||
|
value *secretv0alpha1.ExposedSecureValue
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DecryptResult) Error() error {
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DecryptResult) Value() *secretv0alpha1.ExposedSecureValue {
|
||||||
|
return d.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDecryptResultErr(err error) DecryptResult {
|
||||||
|
return DecryptResult{err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDecryptResultValue(value *secretv0alpha1.ExposedSecureValue) DecryptResult {
|
||||||
|
return DecryptResult{value: value}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecryptService is the inferface for the decrypt service.
|
||||||
|
type DecryptService interface {
|
||||||
|
Decrypt(ctx context.Context, namespace string, names ...string) (map[string]DecryptResult, error)
|
||||||
|
}
|
Reference in New Issue
Block a user