Secrets: Remove temporary allowlist (#108118)
This commit is contained in:
@@ -23,6 +23,3 @@ type DecryptStorage interface {
|
||||
type DecryptAuthorizer interface {
|
||||
Authorize(ctx context.Context, secureValueName string, secureValueDecrypters []string) (identity string, allowed bool)
|
||||
}
|
||||
|
||||
// TEMPORARY: Needed to pass it with wire.
|
||||
type DecryptAllowList map[string]struct{}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package decrypt
|
||||
|
||||
import "github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
|
||||
// TEMPORARY
|
||||
func ProvideDecryptAllowList() contracts.DecryptAllowList {
|
||||
return make(map[string]struct{}, 0)
|
||||
}
|
||||
@@ -15,14 +15,12 @@ import (
|
||||
|
||||
// decryptAuthorizer is the authorizer implementation for decrypt operations.
|
||||
type decryptAuthorizer struct {
|
||||
tracer trace.Tracer
|
||||
allowList contracts.DecryptAllowList
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
func ProvideDecryptAuthorizer(tracer trace.Tracer, allowList contracts.DecryptAllowList) contracts.DecryptAuthorizer {
|
||||
func ProvideDecryptAuthorizer(tracer trace.Tracer) contracts.DecryptAuthorizer {
|
||||
return &decryptAuthorizer{
|
||||
tracer: tracer,
|
||||
allowList: allowList,
|
||||
tracer: tracer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,15 +54,9 @@ func (a *decryptAuthorizer) Authorize(ctx context.Context, secureValueName strin
|
||||
return "", false
|
||||
}
|
||||
|
||||
serviceIdentity := serviceIdentityList[0]
|
||||
|
||||
// TEMPORARY: while we can't onboard every app into secrets, we can block them from decrypting
|
||||
// securevalues preemptively here before even reaching out to the database.
|
||||
// This check can be removed once we open the gates for any service to use secrets.
|
||||
if len(a.allowList) > 0 {
|
||||
if _, exists := a.allowList[serviceIdentity]; !exists || serviceIdentity == "" {
|
||||
return serviceIdentity, false
|
||||
}
|
||||
serviceIdentity := strings.TrimSpace(serviceIdentityList[0])
|
||||
if len(serviceIdentity) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Checks whether the token has the permission to decrypt secure values.
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when no auth info is present, it returns false", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
require.Empty(t, identity)
|
||||
@@ -26,7 +26,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when token permissions are empty, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
@@ -35,7 +35,16 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when service identity is empty, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "", []string{})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
require.Empty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when service identity is empty string, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), " ", []string{})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
require.Empty(t, identity)
|
||||
@@ -43,7 +52,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("when permission format is malformed (missing verb), it returns false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues"})
|
||||
@@ -59,7 +68,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("when permission verb is not exactly `decrypt`, it returns false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues:*"})
|
||||
@@ -76,7 +85,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when permission does not have 2 or 3 parts, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
@@ -85,7 +94,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when permission has group that is not `secret.grafana.app`, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"wrong.group/securevalues/invalid:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
@@ -93,7 +102,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("when permission has resource that is not `securevalues`, it returns false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/invalid-resource:decrypt"})
|
||||
@@ -110,24 +119,15 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when the allow list is empty, it allows all identities", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
require.NotEmpty(t, identity)
|
||||
require.True(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when the identity is not in the allow list, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, map[string]struct{}{"allowed1": {}})
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when the identity doesn't match any allowed decrypters, it returns false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, map[string]struct{}{"identity": {}})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
@@ -143,7 +143,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("when the identity matches an allowed decrypter, it returns true", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, map[string]struct{}{"identity": {}})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
@@ -166,7 +166,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
"wrong.group/securevalues/group2:decrypt",
|
||||
"secret.grafana.app/securevalues/identity:decrypt", // old style of identity+permission
|
||||
})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, map[string]struct{}{"identity": {}})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "name1", []string{"identity"})
|
||||
require.True(t, allowed)
|
||||
@@ -179,7 +179,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when empty secure value name with specific permission, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues/name:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, map[string]struct{}{"identity": {}})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
require.Equal(t, "identity", identity)
|
||||
@@ -188,7 +188,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when permission has an extra / but no name, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues/:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, map[string]struct{}{"identity": {}})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
require.Equal(t, "identity", identity)
|
||||
@@ -197,7 +197,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when the decrypters list is empty, meaning nothing can decrypt the secure value, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, map[string]struct{}{"identity": {}})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "name", []string{})
|
||||
require.Equal(t, "identity", identity)
|
||||
@@ -206,7 +206,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when one of decrypters matches the identity, it returns true", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity1", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, map[string]struct{}{"identity1": {}, "identity2": {}})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity1", "identity2", "identity3"})
|
||||
require.Equal(t, "identity1", identity)
|
||||
@@ -214,7 +214,7 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("permissions must be case-sensitive and return false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, map[string]struct{}{"identity": {}})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"SECRET.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
|
||||
@@ -6,13 +6,13 @@ import (
|
||||
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
encryptionstorage "github.com/grafana/grafana/pkg/storage/secret/encryption"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/decrypt"
|
||||
@@ -28,14 +28,13 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/secret/database"
|
||||
encryptionstorage "github.com/grafana/grafana/pkg/storage/secret/encryption"
|
||||
"github.com/grafana/grafana/pkg/storage/secret/metadata"
|
||||
"github.com/grafana/grafana/pkg/storage/secret/migrator"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type SetupConfig struct {
|
||||
KeeperService contracts.KeeperService
|
||||
AllowList map[string]struct{}
|
||||
}
|
||||
|
||||
func defaultSetupCfg() SetupConfig {
|
||||
@@ -121,7 +120,7 @@ func Setup(t *testing.T, opts ...func(*SetupConfig)) Sut {
|
||||
|
||||
secureValueService := service.ProvideSecureValueService(tracer, accessClient, database, secureValueMetadataStorage, keeperMetadataStorage, keeperService)
|
||||
|
||||
decryptAuthorizer := decrypt.ProvideDecryptAuthorizer(tracer, setupCfg.AllowList)
|
||||
decryptAuthorizer := decrypt.ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
decryptStorage, err := metadata.ProvideDecryptStorage(features, tracer, keeperService, keeperMetadataStorage, secureValueMetadataStorage, decryptAuthorizer, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -2,7 +2,6 @@ package validator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -14,16 +13,12 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
)
|
||||
|
||||
type secureValueValidator struct {
|
||||
decryptersAllowList contracts.DecryptAllowList
|
||||
}
|
||||
type secureValueValidator struct{}
|
||||
|
||||
var _ contracts.SecureValueValidator = &secureValueValidator{}
|
||||
|
||||
func ProvideSecureValueValidator(decryptersAllowList contracts.DecryptAllowList) contracts.SecureValueValidator {
|
||||
return &secureValueValidator{
|
||||
decryptersAllowList: decryptersAllowList,
|
||||
}
|
||||
func ProvideSecureValueValidator() contracts.SecureValueValidator {
|
||||
return &secureValueValidator{}
|
||||
}
|
||||
|
||||
func (v *secureValueValidator) Validate(sv, oldSv *secretv1beta1.SecureValue, operation admission.Operation) field.ErrorList {
|
||||
@@ -50,7 +45,7 @@ func (v *secureValueValidator) Validate(sv, oldSv *secretv1beta1.SecureValue, op
|
||||
)
|
||||
}
|
||||
|
||||
if errs := validateDecrypters(sv.Spec.Decrypters, v.decryptersAllowList); len(errs) > 0 {
|
||||
if errs := validateDecrypters(sv.Spec.Decrypters); len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -107,7 +102,7 @@ func validateSecureValueUpdate(sv, oldSv *secretv1beta1.SecureValue) field.Error
|
||||
}
|
||||
|
||||
// validateDecrypters validates that (if populated) the `decrypters` must be unique.
|
||||
func validateDecrypters(decrypters []string, decryptersAllowList map[string]struct{}) field.ErrorList {
|
||||
func validateDecrypters(decrypters []string) field.ErrorList {
|
||||
errs := make(field.ErrorList, 0)
|
||||
|
||||
// Limit the number of decrypters to 64 to not have it unbounded.
|
||||
@@ -134,20 +129,6 @@ func validateDecrypters(decrypters []string, decryptersAllowList map[string]stru
|
||||
continue
|
||||
}
|
||||
|
||||
// Allow List: decrypters must match exactly and be in the allowed list to be able to decrypt.
|
||||
if len(decryptersAllowList) > 0 {
|
||||
if _, exists := decryptersAllowList[decrypter]; !exists {
|
||||
errs = append(
|
||||
errs,
|
||||
field.Invalid(field.NewPath("spec", "decrypters", "["+strconv.Itoa(i)+"]"), decrypter, fmt.Sprintf("allowed values: %v", decryptersAllowList)),
|
||||
)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Use the same validation as labels for the decrypters.
|
||||
if verrs := validation.IsValidLabelValue(decrypter); len(verrs) > 0 {
|
||||
for _, verr := range verrs {
|
||||
|
||||
@@ -2,8 +2,6 @@ package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -31,7 +29,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
sv := validSecureValue.DeepCopy()
|
||||
sv.Spec.Description = ""
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec.description", errs[0].Field)
|
||||
@@ -43,7 +41,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
sv.Spec.Value = nil
|
||||
sv.Spec.Ref = nil
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec", errs[0].Field)
|
||||
@@ -52,7 +50,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
sv.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue(""))
|
||||
sv.Spec.Ref = nil
|
||||
|
||||
validator = ProvideSecureValueValidator(nil)
|
||||
validator = ProvideSecureValueValidator()
|
||||
errs = validator.Validate(sv, nil, admission.Create)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec", errs[0].Field)
|
||||
@@ -62,7 +60,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
sv.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("value"))
|
||||
sv.Spec.Ref = &ref
|
||||
|
||||
validator = ProvideSecureValueValidator(nil)
|
||||
validator = ProvideSecureValueValidator()
|
||||
errs = validator.Validate(sv, nil, admission.Create)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec", errs[0].Field)
|
||||
@@ -73,7 +71,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
sv.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue(strings.Repeat("a", contracts.SECURE_VALUE_RAW_INPUT_MAX_SIZE_BYTES+1)))
|
||||
sv.Spec.Ref = nil
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec.value", errs[0].Field)
|
||||
@@ -95,7 +93,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, oldSv, admission.Update)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec", errs[0].Field)
|
||||
@@ -115,7 +113,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, oldSv, admission.Update)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec", errs[0].Field)
|
||||
@@ -137,7 +135,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, oldSv, admission.Update)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec", errs[0].Field)
|
||||
@@ -148,7 +146,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
validator = ProvideSecureValueValidator(nil)
|
||||
validator = ProvideSecureValueValidator()
|
||||
errs = validator.Validate(sv, oldSv, admission.Update)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec", errs[0].Field)
|
||||
@@ -167,7 +165,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, oldSv, admission.Update)
|
||||
require.Empty(t, errs)
|
||||
})
|
||||
@@ -175,7 +173,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
t.Run("when the old object is `nil` it returns an error", func(t *testing.T) {
|
||||
sv := &secretv1beta1.SecureValue{}
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, nil, admission.Update)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec", errs[0].Field)
|
||||
@@ -196,7 +194,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, oldSv, admission.Update)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec", errs[0].Field)
|
||||
@@ -216,75 +214,25 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec.decrypters.[1]", errs[0].Field)
|
||||
})
|
||||
|
||||
t.Run("when set, the `decrypters` must be one of the allowed in the allow list", func(t *testing.T) {
|
||||
allowList := map[string]struct{}{"app1": {}, "app2": {}}
|
||||
decrypters := slices.Collect(maps.Keys(allowList))
|
||||
t.Run("`decrypters` list can be empty", func(t *testing.T) {
|
||||
ref := "ref"
|
||||
sv := &secretv1beta1.SecureValue{
|
||||
Spec: secretv1beta1.SecureValueSpec{
|
||||
Description: "description", Ref: &ref,
|
||||
|
||||
t.Run("no matches, returns an error", func(t *testing.T) {
|
||||
ref := "ref"
|
||||
sv := &secretv1beta1.SecureValue{
|
||||
Spec: secretv1beta1.SecureValueSpec{
|
||||
Description: "description", Ref: &ref,
|
||||
Decrypters: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
Decrypters: []string{"app3"},
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(allowList)
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Len(t, errs, 1)
|
||||
})
|
||||
|
||||
t.Run("no decrypters, returns no error", func(t *testing.T) {
|
||||
ref := "ref"
|
||||
sv := &secretv1beta1.SecureValue{
|
||||
Spec: secretv1beta1.SecureValueSpec{
|
||||
Description: "description", Ref: &ref,
|
||||
|
||||
Decrypters: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(allowList)
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Empty(t, errs)
|
||||
})
|
||||
|
||||
t.Run("one match, returns no errors", func(t *testing.T) {
|
||||
ref := "ref"
|
||||
sv := &secretv1beta1.SecureValue{
|
||||
Spec: secretv1beta1.SecureValueSpec{
|
||||
Description: "description", Ref: &ref,
|
||||
|
||||
Decrypters: []string{decrypters[0]},
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(allowList)
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Empty(t, errs)
|
||||
})
|
||||
|
||||
t.Run("all matches, returns no errors", func(t *testing.T) {
|
||||
ref := "ref"
|
||||
sv := &secretv1beta1.SecureValue{
|
||||
Spec: secretv1beta1.SecureValueSpec{
|
||||
Description: "description", Ref: &ref,
|
||||
|
||||
Decrypters: decrypters,
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(allowList)
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Empty(t, errs)
|
||||
})
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Empty(t, errs)
|
||||
})
|
||||
|
||||
t.Run("`decrypters` must be a valid label value", func(t *testing.T) {
|
||||
@@ -310,7 +258,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Len(t, errs, 3)
|
||||
})
|
||||
@@ -330,7 +278,7 @@ func TestValidateSecureValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
validator := ProvideSecureValueValidator(nil)
|
||||
validator := ProvideSecureValueValidator()
|
||||
errs := validator.Validate(sv, nil, admission.Create)
|
||||
require.Len(t, errs, 1)
|
||||
require.Equal(t, "spec.decrypters", errs[0].Field)
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api"
|
||||
"github.com/grafana/grafana/pkg/api/avatar"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
@@ -426,7 +425,6 @@ var wireBasicSet = wire.NewSet(
|
||||
secretmetadata.ProvideKeeperMetadataStorage,
|
||||
secretmetadata.ProvideDecryptStorage,
|
||||
secretdecrypt.ProvideDecryptAuthorizer,
|
||||
secretdecrypt.ProvideDecryptAllowList,
|
||||
secretdecrypt.ProvideDecryptService,
|
||||
secretencryption.ProvideDataKeyStorage,
|
||||
secretencryption.ProvideEncryptedValueStorage,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -44,53 +44,13 @@ func TestIntegrationDecrypt(t *testing.T) {
|
||||
// Create auth context with proper permissions
|
||||
authCtx := createAuthContext(ctx, "default", []string{"secret.grafana.app/securevalues/group1:decrypt"}, "svc", types.TypeUser)
|
||||
|
||||
sut := testutils.Setup(t, testutils.WithMutateCfg(func(sc *testutils.SetupConfig) {
|
||||
sc.AllowList = map[string]struct{}{"group1": {}}
|
||||
}))
|
||||
sut := testutils.Setup(t)
|
||||
|
||||
exposed, err := sut.DecryptStorage.Decrypt(authCtx, "default", "non-existent-value")
|
||||
require.ErrorIs(t, err, contracts.ErrDecryptNotFound)
|
||||
require.Empty(t, exposed)
|
||||
})
|
||||
|
||||
t.Run("when auth info is not in allowlist, it returns an unauthorized error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
svName := "sv-test"
|
||||
svcIdentity := "svc"
|
||||
|
||||
// Create auth context with identity that is not in allowlist
|
||||
authCtx := createAuthContext(ctx, "default", []string{"secret.grafana.app/securevalues/" + svName + ":decrypt"}, svcIdentity, types.TypeUser)
|
||||
|
||||
// Create an allowlist that doesn't include the permission
|
||||
allowList := map[string]struct{}{"allowed-group": {}}
|
||||
|
||||
// Setup service
|
||||
sut := testutils.Setup(t, testutils.WithMutateCfg(func(sc *testutils.SetupConfig) {
|
||||
sc.AllowList = allowList
|
||||
}))
|
||||
|
||||
// Create a secure value that is not in the allowlist
|
||||
spec := secretv1beta1.SecureValueSpec{
|
||||
Description: "description",
|
||||
Decrypters: []string{svcIdentity},
|
||||
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
|
||||
}
|
||||
sv := &secretv1beta1.SecureValue{Spec: spec}
|
||||
sv.Name = svName
|
||||
sv.Namespace = "default"
|
||||
|
||||
_, err := sut.CreateSv(authCtx, testutils.CreateSvWithSv(sv))
|
||||
require.NoError(t, err)
|
||||
|
||||
exposed, err := sut.DecryptStorage.Decrypt(authCtx, "default", svName)
|
||||
require.ErrorIs(t, err, contracts.ErrDecryptNotAuthorized)
|
||||
require.Empty(t, exposed)
|
||||
})
|
||||
|
||||
t.Run("when happy path with valid auth and permissions, it returns decrypted value", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -102,15 +62,10 @@ func TestIntegrationDecrypt(t *testing.T) {
|
||||
// Create auth context with proper permissions that match the decrypters
|
||||
authCtx := createAuthContext(ctx, "default", []string{"secret.grafana.app/securevalues:decrypt"}, svcIdentity, types.TypeUser)
|
||||
|
||||
// Include the group in allowlist
|
||||
allowList := map[string]struct{}{svcIdentity: {}}
|
||||
|
||||
// Setup service
|
||||
sut := testutils.Setup(t, testutils.WithMutateCfg(func(sc *testutils.SetupConfig) {
|
||||
sc.AllowList = allowList
|
||||
}))
|
||||
sut := testutils.Setup(t)
|
||||
|
||||
// Create a secure value that is in the allowlist
|
||||
// Create a secure value
|
||||
spec := secretv1beta1.SecureValueSpec{
|
||||
Description: "description",
|
||||
Decrypters: []string{svcIdentity},
|
||||
@@ -141,15 +96,10 @@ func TestIntegrationDecrypt(t *testing.T) {
|
||||
// Create auth context with proper permissions that match the decrypters
|
||||
authCtx := createAuthContext(ctx, "default", []string{"secret.grafana.app/securevalues/sv-test2:decrypt"}, svcIdentity, types.TypeUser)
|
||||
|
||||
// Include the group in allowlist
|
||||
allowList := map[string]struct{}{svcIdentity: {}}
|
||||
|
||||
// Setup service
|
||||
sut := testutils.Setup(t, testutils.WithMutateCfg(func(sc *testutils.SetupConfig) {
|
||||
sc.AllowList = allowList
|
||||
}))
|
||||
sut := testutils.Setup(t)
|
||||
|
||||
// Create a secure value that is in the allowlist
|
||||
// Create a secure value
|
||||
spec := secretv1beta1.SecureValueSpec{
|
||||
Description: "description",
|
||||
Decrypters: []string{svcIdentity},
|
||||
|
||||
Reference in New Issue
Block a user