SecretService: Add decrypt service (#104472)

* initial decrypt files

* service tests

* fix lint

* move interface to secret service folder
pull/105171/head
Dana Axinte 3 weeks ago committed by GitHub
parent 1fa74d2427
commit 1d19e3616f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 36
      pkg/registry/apis/secret/decrypt/service.go
  2. 101
      pkg/registry/apis/secret/decrypt/service_test.go
  3. 36
      pkg/registry/apis/secret/service/decrypt.go

@ -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
}

@ -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)
}

@ -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)
}
Loading…
Cancel
Save