SecretsManager: Various utils for usage insights, outbox and secretkeeper (#106010)
* SecretsManager: utils for usage insights on ST mode Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com> * SecretsManager: add assert Co-authored-by: PoorlyDefinedBehaviour <brunotj2015@hotmail.com> * SecretsManager: Remove encryption scope option Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com> * SecretsManager: add fake keeper Co-authored-by: Dana Axinte <53751979+dana-axinte@users.noreply.github.com> Co-authored-by: PoorlyDefinedBehaviour <brunotj2015@hotmail.com> Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com> --------- Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com> Co-authored-by: PoorlyDefinedBehaviour <brunotj2015@hotmail.com>
This commit is contained in:
co-authored by
Matheus Macabu
PoorlyDefinedBehaviour
parent
45c361e307
commit
b4cd51810b
@@ -0,0 +1,17 @@
|
||||
package usagestats
|
||||
|
||||
import "context"
|
||||
|
||||
type NoopUsageStats struct{}
|
||||
|
||||
var _ Service = &NoopUsageStats{}
|
||||
|
||||
func (usm *NoopUsageStats) RegisterMetricsFunc(_ MetricsFunc) {}
|
||||
|
||||
func (usm *NoopUsageStats) GetUsageReport(_ context.Context) (Report, error) {
|
||||
return Report{}, nil
|
||||
}
|
||||
|
||||
func (usm *NoopUsageStats) RegisterSendReportCallback(_ SendReportCallbackFunc) {}
|
||||
|
||||
func (usm *NoopUsageStats) SetReadyToReport(_ context.Context) {}
|
||||
@@ -0,0 +1,26 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func True(expr bool, msg string, args ...any) {
|
||||
if !expr {
|
||||
if len(args) > 0 {
|
||||
panic(fmt.Sprintf(msg, args...))
|
||||
} else {
|
||||
panic(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorIs(err1, err2 error) {
|
||||
if !errors.Is(err1, err2) {
|
||||
panic(fmt.Sprintf("expected error %T(%+v) to be %T(%+v)", err1, err1, err2, err2))
|
||||
}
|
||||
}
|
||||
|
||||
func Equal[T comparable](v1 T, v2 T, msg string) {
|
||||
True(v1 == v2, "expected %+v to equal %+v: %s", v1, v2, msg)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTrue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.PanicsWithValue(t, "error msg", func() {
|
||||
True(1 == 2, "error msg")
|
||||
})
|
||||
|
||||
require.PanicsWithValue(t, "error msg 1", func() {
|
||||
True(1 == 2, "error msg %d", 1)
|
||||
})
|
||||
|
||||
require.NotPanics(t, func() {
|
||||
True(true, "oops")
|
||||
})
|
||||
}
|
||||
|
||||
func TestErrorIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := errors.New("some error")
|
||||
|
||||
require.PanicsWithValue(t, "expected error *errors.errorString(other error) to be *errors.errorString(some error)", func() {
|
||||
ErrorIs(fmt.Errorf("other error"), err)
|
||||
})
|
||||
|
||||
require.NotPanics(t, func() {
|
||||
ErrorIs(err, err)
|
||||
ErrorIs(fmt.Errorf("something: %w", err), err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.PanicsWithValue(t, "expected 1 to equal 2: details", func() {
|
||||
Equal(1, 2, "details")
|
||||
})
|
||||
|
||||
require.NotPanics(t, func() {
|
||||
Equal("a", "a", "details")
|
||||
})
|
||||
}
|
||||
@@ -8,31 +8,13 @@ type EncryptionManager interface {
|
||||
// For those specific use cases where the encryption operation cannot be moved outside
|
||||
// the database transaction, look at database-specific methods present at the specific
|
||||
// implementation present at manager.EncryptionService.
|
||||
Encrypt(ctx context.Context, namespace string, payload []byte, opt EncryptionOptions) ([]byte, error)
|
||||
Encrypt(ctx context.Context, namespace string, payload []byte) ([]byte, error)
|
||||
Decrypt(ctx context.Context, namespace string, payload []byte) ([]byte, error)
|
||||
|
||||
RotateDataKeys(ctx context.Context, namespace string) error
|
||||
ReEncryptDataKeys(ctx context.Context, namespace string) error
|
||||
}
|
||||
|
||||
type EncryptionOptions func() string
|
||||
|
||||
// EncryptWithoutScope uses a root level data key for encryption (DEK),
|
||||
// in other words this DEK is not bound to any specific scope (not attached to any user, org, etc.).
|
||||
func EncryptWithoutScope() EncryptionOptions {
|
||||
return func() string {
|
||||
return "root"
|
||||
}
|
||||
}
|
||||
|
||||
// EncryptWithScope uses a data key for encryption bound to some specific scope (i.e., user, org, etc.).
|
||||
// Scope should look like "user:10", "org:1".
|
||||
func EncryptWithScope(scope string) EncryptionOptions {
|
||||
return func() string {
|
||||
return scope
|
||||
}
|
||||
}
|
||||
|
||||
type EncryptedValue struct {
|
||||
UID string
|
||||
Namespace string
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package fakes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
)
|
||||
|
||||
var ErrSecretNotFound = errors.New("secret not found")
|
||||
|
||||
type FakeKeeper struct {
|
||||
values map[string]map[string]string
|
||||
}
|
||||
|
||||
var _ contracts.Keeper = (*FakeKeeper)(nil)
|
||||
|
||||
func NewFakeKeeper() *FakeKeeper {
|
||||
return &FakeKeeper{
|
||||
values: make(map[string]map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FakeKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) {
|
||||
ns, ok := s.values[namespace]
|
||||
if !ok {
|
||||
ns = make(map[string]string)
|
||||
}
|
||||
uid := uuid.New().String()
|
||||
ns[uid] = exposedValueOrRef
|
||||
s.values[namespace] = ns
|
||||
|
||||
return contracts.ExternalID(uid), nil
|
||||
}
|
||||
|
||||
func (s *FakeKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID) (secretv0alpha1.ExposedSecureValue, error) {
|
||||
ns, ok := s.values[namespace]
|
||||
if !ok {
|
||||
return "", ErrSecretNotFound
|
||||
}
|
||||
exposedVal, ok := ns[externalID.String()]
|
||||
if !ok {
|
||||
return "", ErrSecretNotFound
|
||||
}
|
||||
|
||||
return secretv0alpha1.NewExposedSecureValue(exposedVal), nil
|
||||
}
|
||||
|
||||
func (s *FakeKeeper) Delete(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FakeKeeper) Update(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID, exposedValueOrRef string) error {
|
||||
ns, ok := s.values[namespace]
|
||||
if !ok {
|
||||
return ErrSecretNotFound
|
||||
}
|
||||
_, ok = ns[externalID.String()]
|
||||
if !ok {
|
||||
return ErrSecretNotFound
|
||||
}
|
||||
|
||||
ns[externalID.String()] = exposedValueOrRef
|
||||
return nil
|
||||
}
|
||||
@@ -29,11 +29,12 @@ func NewSQLKeeper(
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SQLKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) {
|
||||
// TODO: parameter cfg is not being used
|
||||
func (s *SQLKeeper) Store(ctx context.Context, _ secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "sqlKeeper.Store")
|
||||
defer span.End()
|
||||
|
||||
encryptedData, err := s.encryptionManager.Encrypt(ctx, namespace, []byte(exposedValueOrRef), contracts.EncryptWithoutScope())
|
||||
encryptedData, err := s.encryptionManager.Encrypt(ctx, namespace, []byte(exposedValueOrRef))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to encrypt value: %w", err)
|
||||
}
|
||||
@@ -79,7 +80,7 @@ func (s *SQLKeeper) Update(ctx context.Context, cfg secretv0alpha1.KeeperConfig,
|
||||
ctx, span := s.tracer.Start(ctx, "sqlKeeper.Update")
|
||||
defer span.End()
|
||||
|
||||
encryptedData, err := s.encryptionManager.Encrypt(ctx, namespace, []byte(exposedValueOrRef), contracts.EncryptWithoutScope())
|
||||
encryptedData, err := s.encryptionManager.Encrypt(ctx, namespace, []byte(exposedValueOrRef))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to encrypt value: %w", err)
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ func setupTestService(t *testing.T, cfg *setting.Cfg) (*SQLKeeper, error) {
|
||||
// While we don't have the real implementation, use an in-memory one
|
||||
type inMemoryEncryptionManager struct{}
|
||||
|
||||
func (m *inMemoryEncryptionManager) Encrypt(_ context.Context, _ string, value []byte, _ contracts.EncryptionOptions) ([]byte, error) {
|
||||
func (m *inMemoryEncryptionManager) Encrypt(_ context.Context, _ string, value []byte) ([]byte, error) {
|
||||
return []byte(base64.StdEncoding.EncodeToString(value)), nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user