Secrets: Add namespace matches checks to authorizer and secure value client (#109651)
* Decrypt: Add namespace matches to authorizer * SecureValueClient: Add namespace matches when auth checking
This commit is contained in:
1 parent
a38a5e0301
commit
dfae5e5b4d
7 files changed
+145
-103
No files matched your search
@@ -24,7 +24,7 @@ type DecryptStorage interface {
|
||||
|
||||
// DecryptAuthorizer is the interface for authorizing decryption requests.
|
||||
type DecryptAuthorizer interface {
|
||||
Authorize(ctx context.Context, secureValueName string, secureValueDecrypters []string) (identity string, allowed bool)
|
||||
Authorize(ctx context.Context, namespace xkube.Namespace, secureValueName string, secureValueDecrypters []string) (identity string, allowed bool)
|
||||
}
|
||||
|
||||
// DecryptService is the interface for the decrypt service.
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
|
||||
)
|
||||
|
||||
// decryptAuthorizer is the authorizer implementation for decrypt operations.
|
||||
@@ -25,7 +26,7 @@ func ProvideDecryptAuthorizer(tracer trace.Tracer) contracts.DecryptAuthorizer {
|
||||
}
|
||||
|
||||
// authorize checks whether the auth info token has the right permissions to decrypt the secure value.
|
||||
func (a *decryptAuthorizer) Authorize(ctx context.Context, secureValueName string, secureValueDecrypters []string) (id string, isAllowed bool) {
|
||||
func (a *decryptAuthorizer) Authorize(ctx context.Context, ns xkube.Namespace, secureValueName string, secureValueDecrypters []string) (id string, isAllowed bool) {
|
||||
ctx, span := a.tracer.Start(ctx, "DecryptAuthorizer.Authorize", trace.WithAttributes(
|
||||
attribute.String("name", secureValueName),
|
||||
attribute.StringSlice("decrypters", secureValueDecrypters),
|
||||
@@ -44,6 +45,10 @@ func (a *decryptAuthorizer) Authorize(ctx context.Context, secureValueName strin
|
||||
return "", false
|
||||
}
|
||||
|
||||
if !claims.NamespaceMatches(authInfo.GetNamespace(), ns.String()) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
serviceIdentityList, ok := authInfo.GetExtra()[authn.ServiceIdentityKey]
|
||||
if !ok {
|
||||
return "", false
|
||||
|
||||
@@ -10,43 +10,45 @@ import (
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
|
||||
)
|
||||
|
||||
func TestDecryptAuthorizer(t *testing.T) {
|
||||
tracer := noop.NewTracerProvider().Tracer("test")
|
||||
defaultNs := xkube.Namespace("default")
|
||||
|
||||
t.Run("when no auth info is present, it returns false", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.Empty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when token permissions are empty, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when service identity is empty, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "", []string{})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "", []string{})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", 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{})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), " ", []string{})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.Empty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
@@ -55,14 +57,14 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues"})
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
// named
|
||||
ctx = createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues/name"})
|
||||
identity, allowed = authorizer.Authorize(ctx, "", nil)
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/name"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
@@ -71,32 +73,32 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues:*"})
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:*"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
// named
|
||||
ctx = createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues/name:something"})
|
||||
identity, allowed = authorizer.Authorize(ctx, "", nil)
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/name:something"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
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"})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
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"})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"wrong.group/securevalues/invalid:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
@@ -105,23 +107,23 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/invalid-resource:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, "", nil)
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/invalid-resource:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
// named
|
||||
ctx = createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/invalid-resource/name:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, "", nil)
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/invalid-resource/name:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
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"})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
require.NotEmpty(t, identity)
|
||||
require.True(t, allowed)
|
||||
})
|
||||
@@ -130,14 +132,14 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"group2"})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"group2"})
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
// named
|
||||
ctx = createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues/name:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, "", []string{"group2"})
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/name:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", []string{"group2"})
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
@@ -146,20 +148,20 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
require.True(t, allowed)
|
||||
require.Equal(t, "identity", identity)
|
||||
|
||||
// named
|
||||
ctx = createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues/name:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, "name", []string{"identity"})
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/name:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "name", []string{"identity"})
|
||||
require.True(t, allowed)
|
||||
require.Equal(t, "identity", identity)
|
||||
})
|
||||
|
||||
t.Run("when there are multiple permissions, some invalid, only valid ones are considered", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{
|
||||
"secret.grafana.app/securevalues/name1:decrypt",
|
||||
"secret.grafana.app/securevalues/name2:decrypt",
|
||||
"secret.grafana.app/securevalues/invalid:read",
|
||||
@@ -168,47 +170,47 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "name1", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "name1", []string{"identity"})
|
||||
require.True(t, allowed)
|
||||
require.Equal(t, "identity", identity)
|
||||
|
||||
identity, allowed = authorizer.Authorize(ctx, "name2", []string{"identity"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "name2", []string{"identity"})
|
||||
require.True(t, allowed)
|
||||
require.Equal(t, "identity", identity)
|
||||
})
|
||||
|
||||
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"})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/name:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
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"})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
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"})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "name", []string{})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "name", []string{})
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
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"})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity1", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity1", "identity2", "identity3"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity1", "identity2", "identity3"})
|
||||
require.Equal(t, "identity1", identity)
|
||||
require.True(t, allowed)
|
||||
})
|
||||
@@ -216,25 +218,35 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
t.Run("permissions must be case-sensitive and return false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
ctx := createAuthContext(context.Background(), "identity", []string{"SECRET.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"SECRET.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
ctx = createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/SECUREVALUES:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/SECUREVALUES:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
ctx = createAuthContext(context.Background(), "identity", []string{"secret.grafana.app/securevalues:DECRYPT"})
|
||||
identity, allowed = authorizer.Authorize(ctx, "", []string{"identity"})
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:DECRYPT"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when namespace doesn't match the token's, it returns false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
|
||||
ctx := createAuthContext(context.Background(), "namespace1", "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, "namespace2", "", []string{"identity"})
|
||||
require.Empty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
}
|
||||
|
||||
func createAuthContext(ctx context.Context, serviceIdentity string, permissions []string) context.Context {
|
||||
func createAuthContext(ctx context.Context, namespace string, serviceIdentity string, permissions []string) context.Context {
|
||||
requester := &identity.StaticRequester{
|
||||
Namespace: namespace,
|
||||
AccessTokenClaims: &authn.Claims[authn.AccessTokenClaims]{
|
||||
Rest: authn.AccessTokenClaims{
|
||||
Permissions: permissions,
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
|
||||
)
|
||||
|
||||
// NoopAlwaysAllowedAuthorizer is a no-op implementation of the DecryptAuthorizer which always returns `allowed=true`.
|
||||
@@ -11,6 +12,6 @@ type NoopAlwaysAllowedAuthorizer struct{}
|
||||
|
||||
var _ contracts.DecryptAuthorizer = &NoopAlwaysAllowedAuthorizer{}
|
||||
|
||||
func (a *NoopAlwaysAllowedAuthorizer) Authorize(ctx context.Context, secureValueName string, secureValueDecrypters []string) (identity string, allowed bool) {
|
||||
func (a *NoopAlwaysAllowedAuthorizer) Authorize(context.Context, xkube.Namespace, string, []string) (string, bool) {
|
||||
return "", true
|
||||
}
|
||||
@@ -265,8 +265,17 @@ func (c *secureValueClient) mapError(err error, name string) error {
|
||||
}
|
||||
|
||||
func (c *secureValueClient) checkAccess(ctx context.Context, name, verb string) error {
|
||||
authInfo, ok := claims.AuthInfoFrom(ctx)
|
||||
if !ok {
|
||||
return apierrors.NewUnauthorized("missing auth info in context")
|
||||
}
|
||||
|
||||
gr := secretv1beta1.SecureValuesResourceInfo.GroupResource()
|
||||
|
||||
if !claims.NamespaceMatches(authInfo.GetNamespace(), c.namespace) {
|
||||
return apierrors.NewForbidden(gr, name, fmt.Errorf("namespace mismatch: %s != %s", authInfo.GetNamespace(), c.namespace))
|
||||
}
|
||||
|
||||
decision, reason, err := c.access.Authorize(ctx, authorizer.AttributesRecord{
|
||||
Verb: verb,
|
||||
Namespace: c.namespace,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -118,61 +119,75 @@ func TestIntegration_SecureValueClient_CRUD(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_SecureValueClient_CRUD_NoPermissions(t *testing.T) {
|
||||
setup := testutils.Setup(t)
|
||||
|
||||
validator := validator.ProvideSecureValueValidator()
|
||||
|
||||
client := ProvideSecureValueClient(
|
||||
setup.SecureValueService,
|
||||
validator,
|
||||
setup.AccessClient,
|
||||
)
|
||||
|
||||
ns := "stacks-1234"
|
||||
ctx := testutils.CreateUserAuthContext(t.Context(), ns, nil)
|
||||
|
||||
nsClient, err := client.Client(ctx, ns)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, nsClient)
|
||||
|
||||
sv := &secretv1beta1.SecureValue{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-sv",
|
||||
Namespace: ns,
|
||||
},
|
||||
testcases := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
errorReason metav1.StatusReason
|
||||
}{
|
||||
{"no auth context", context.Background(), metav1.StatusReasonUnauthorized},
|
||||
{"no permissions", testutils.CreateUserAuthContext(t.Context(), ns, nil), metav1.StatusReasonForbidden},
|
||||
{"mismatching namespace", testutils.CreateUserAuthContext(t.Context(), "other-ns", nil), metav1.StatusReasonForbidden},
|
||||
}
|
||||
|
||||
unstructured, err := toUnstructured(sv)
|
||||
require.NoError(t, err)
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
setup := testutils.Setup(t)
|
||||
|
||||
// Create
|
||||
created, err := nsClient.Create(ctx, unstructured, metav1.CreateOptions{})
|
||||
var apiErr *apierrors.StatusError
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, apiErr.ErrStatus.Reason, metav1.StatusReasonForbidden)
|
||||
require.Nil(t, created)
|
||||
validator := validator.ProvideSecureValueValidator()
|
||||
|
||||
// Read
|
||||
read, err := nsClient.Get(ctx, sv.Name, metav1.GetOptions{})
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, apiErr.ErrStatus.Reason, metav1.StatusReasonForbidden)
|
||||
require.Nil(t, created)
|
||||
client := ProvideSecureValueClient(
|
||||
setup.SecureValueService,
|
||||
validator,
|
||||
setup.AccessClient,
|
||||
)
|
||||
|
||||
// Update
|
||||
updated, err := nsClient.Update(ctx, unstructured, metav1.UpdateOptions{})
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, apiErr.ErrStatus.Reason, metav1.StatusReasonForbidden)
|
||||
require.Nil(t, updated)
|
||||
ctx := tc.ctx
|
||||
|
||||
// List
|
||||
list, err := nsClient.List(ctx, metav1.ListOptions{})
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, apiErr.ErrStatus.Reason, metav1.StatusReasonForbidden)
|
||||
require.Nil(t, list)
|
||||
nsClient, err := client.Client(ctx, ns)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, nsClient)
|
||||
|
||||
// Delete
|
||||
err = nsClient.Delete(ctx, sv.Name, metav1.DeleteOptions{})
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, apiErr.ErrStatus.Reason, metav1.StatusReasonForbidden)
|
||||
require.Nil(t, read)
|
||||
sv := &secretv1beta1.SecureValue{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-sv",
|
||||
Namespace: ns,
|
||||
},
|
||||
}
|
||||
|
||||
unstructured, err := toUnstructured(sv)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create
|
||||
created, err := nsClient.Create(ctx, unstructured, metav1.CreateOptions{})
|
||||
var apiErr *apierrors.StatusError
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, apiErr.ErrStatus.Reason, tc.errorReason)
|
||||
require.Nil(t, created)
|
||||
|
||||
// Read
|
||||
read, err := nsClient.Get(ctx, sv.Name, metav1.GetOptions{})
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, apiErr.ErrStatus.Reason, tc.errorReason)
|
||||
require.Nil(t, read)
|
||||
|
||||
// Update
|
||||
updated, err := nsClient.Update(ctx, unstructured, metav1.UpdateOptions{})
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, apiErr.ErrStatus.Reason, tc.errorReason)
|
||||
require.Nil(t, updated)
|
||||
|
||||
// List
|
||||
list, err := nsClient.List(ctx, metav1.ListOptions{})
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, apiErr.ErrStatus.Reason, tc.errorReason)
|
||||
require.Nil(t, list)
|
||||
|
||||
// Delete
|
||||
err = nsClient.Delete(ctx, sv.Name, metav1.DeleteOptions{})
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, apiErr.ErrStatus.Reason, tc.errorReason)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ func (s *decryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace,
|
||||
return "", contracts.ErrDecryptNotFound
|
||||
}
|
||||
|
||||
decrypterIdentity, authorized := s.decryptAuthorizer.Authorize(ctx, name, sv.Spec.Decrypters)
|
||||
decrypterIdentity, authorized := s.decryptAuthorizer.Authorize(ctx, namespace, name, sv.Spec.Decrypters)
|
||||
if !authorized {
|
||||
return "", contracts.ErrDecryptNotAuthorized
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user