Files
grafana/pkg/registry/apis/secret/secure_value_client_test.go
T
Matheus Macabu dfae5e5b4d 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
2025-08-14 11:50:56 +02:00

194 lines
5.5 KiB
Go

package secret
import (
"context"
"testing"
"github.com/stretchr/testify/require"
apierrors "k8s.io/apimachinery/pkg/api/errors"
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/registry/apis/secret/testutils"
"github.com/grafana/grafana/pkg/registry/apis/secret/validator"
)
func TestIntegration_SecureValueClient_CRUD(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}
setup := testutils.Setup(t)
validator := validator.ProvideSecureValueValidator()
client := ProvideSecureValueClient(
setup.SecureValueService,
validator,
setup.AccessClient,
)
ns := "stacks-1234"
ctx := testutils.CreateUserAuthContext(t.Context(), ns, map[string][]string{
"securevalues:create": {"securevalues:uid:*"},
"securevalues:read": {"securevalues:uid:*"},
"securevalues:write": {"securevalues:uid:*"},
"securevalues:delete": {"securevalues:uid:*"},
})
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,
},
Spec: secretv1beta1.SecureValueSpec{
Description: "test-description",
Value: ptr.To(secretv1beta1.NewExposedSecureValue("test-value")),
},
}
unstructured, err := toUnstructured(sv)
require.NoError(t, err)
// Create
created, err := nsClient.Create(ctx, unstructured, metav1.CreateOptions{})
require.NoError(t, err)
createdSv, err := fromUnstructured(created)
require.NoError(t, err)
require.NotEmpty(t, createdSv.UID)
require.Nil(t, createdSv.Spec.Value)
require.Equal(t, sv.Name, createdSv.Name)
require.Equal(t, sv.Namespace, createdSv.Namespace)
// Read
read, err := nsClient.Get(ctx, createdSv.Name, metav1.GetOptions{})
require.NoError(t, err)
readSv, err := fromUnstructured(read)
require.NoError(t, err)
require.EqualValues(t, createdSv, readSv)
// Update
updatedSv := &secretv1beta1.SecureValue{
ObjectMeta: metav1.ObjectMeta{
Name: createdSv.Name,
Namespace: createdSv.Namespace,
},
Spec: secretv1beta1.SecureValueSpec{
Description: "test-description-updated",
Value: ptr.To(secretv1beta1.NewExposedSecureValue("test-value-updated")),
},
}
unstructured, err = toUnstructured(updatedSv)
require.NoError(t, err)
_, err = nsClient.Update(ctx, unstructured, metav1.UpdateOptions{})
require.NoError(t, err)
read, err = nsClient.Get(ctx, createdSv.Name, metav1.GetOptions{})
require.NoError(t, err)
readSv, err = fromUnstructured(read)
require.NoError(t, err)
require.Equal(t, updatedSv.Spec.Description, readSv.Spec.Description)
require.Equal(t, updatedSv.Name, readSv.Name)
require.Equal(t, updatedSv.Namespace, readSv.Namespace)
// List
list, err := nsClient.List(ctx, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, list.Items, 1)
require.Equal(t, createdSv.Name, list.Items[0].GetName())
require.Equal(t, createdSv.Namespace, list.Items[0].GetNamespace())
// Delete
err = nsClient.Delete(ctx, createdSv.Name, metav1.DeleteOptions{})
require.NoError(t, err)
read, err = nsClient.Get(ctx, createdSv.Name, metav1.GetOptions{})
var apiErr *apierrors.StatusError
require.ErrorAs(t, err, &apiErr)
require.Equal(t, apiErr.ErrStatus.Reason, metav1.StatusReasonNotFound)
require.Nil(t, read)
}
func Test_SecureValueClient_CRUD_NoPermissions(t *testing.T) {
ns := "stacks-1234"
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},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
setup := testutils.Setup(t)
validator := validator.ProvideSecureValueValidator()
client := ProvideSecureValueClient(
setup.SecureValueService,
validator,
setup.AccessClient,
)
ctx := tc.ctx
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,
},
}
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)
})
}
}