Secrets: Manage secure values inside any resource (#107803)

This commit is contained in:
Ryan McKinley
2025-08-14 12:31:24 +00:00
committed by GitHub
parent d08ea58243
commit d3df5b8ddd
11 changed files with 963 additions and 133 deletions
+61 -28
View File
@@ -15,7 +15,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apiserver/pkg/storage"
authtypes "github.com/grafana/authlib/types"
authlib "github.com/grafana/authlib/types"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
@@ -37,19 +37,19 @@ func TestPrepareObjectForStorage(t *testing.T) {
},
}
ctx := authtypes.WithAuthInfo(context.Background(),
&identity.StaticRequester{UserID: 1, UserUID: "user-uid", Type: authtypes.TypeUser},
ctx := authlib.WithAuthInfo(context.Background(),
&identity.StaticRequester{UserID: 1, UserUID: "user-uid", Type: authlib.TypeUser},
)
t.Run("Error getting auth info from context", func(t *testing.T) {
_, _, err := s.prepareObjectForStorage(context.Background(), nil)
_, err := s.prepareObjectForStorage(context.Background(), nil)
require.Error(t, err)
require.Contains(t, err.Error(), "missing auth info")
})
t.Run("Error on missing name", func(t *testing.T) {
dashboard := dashv1.Dashboard{}
_, _, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
_, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
require.Error(t, err)
require.Contains(t, err.Error(), "missing name")
})
@@ -58,7 +58,7 @@ func TestPrepareObjectForStorage(t *testing.T) {
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
dashboard.ResourceVersion = "123"
_, _, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
_, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
require.Error(t, err)
require.Equal(t, storage.ErrResourceVersionSetOnCreate, err)
})
@@ -67,10 +67,10 @@ func TestPrepareObjectForStorage(t *testing.T) {
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
encodedData, _, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
v, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
require.NoError(t, err)
newObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
newObject, _, err := s.codec.Decode(v.raw.Bytes(), nil, &dashv1.Dashboard{})
require.NoError(t, err)
obj, err := utils.MetaAccessor(newObject)
require.NoError(t, err)
@@ -106,10 +106,10 @@ func TestPrepareObjectForStorage(t *testing.T) {
TimestampMillis: now.UnixMilli(),
})
encodedData, _, err := s.prepareObjectForStorage(ctx, obj)
v, err := s.prepareObjectForStorage(ctx, obj)
require.NoError(t, err)
newObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
newObject, _, err := s.codec.Decode(v.raw.Bytes(), nil, &dashv1.Dashboard{})
require.NoError(t, err)
meta, err = utils.MetaAccessor(newObject)
require.NoError(t, err)
@@ -133,10 +133,10 @@ func TestPrepareObjectForStorage(t *testing.T) {
meta.SetFolder("aaa")
require.NoError(t, err)
encodedData, _, err := s.prepareObjectForStorage(ctx, obj)
v, err := s.prepareObjectForStorage(ctx, obj)
require.NoError(t, err)
insertedObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
insertedObject, _, err := s.codec.Decode(v.raw.Bytes(), nil, &dashv1.Dashboard{})
require.NoError(t, err)
meta, err = utils.MetaAccessor(insertedObject)
require.NoError(t, err)
@@ -148,8 +148,8 @@ func TestPrepareObjectForStorage(t *testing.T) {
require.Nil(t, ts)
// Change the user... and only update metadata
ctx = authtypes.WithAuthInfo(context.Background(),
&identity.StaticRequester{UserID: 1, UserUID: "user2", Type: authtypes.TypeUser},
ctx = authlib.WithAuthInfo(context.Background(),
&identity.StaticRequester{UserID: 1, UserUID: "user2", Type: authlib.TypeUser},
)
// Change the status... but generation is the same
@@ -189,9 +189,9 @@ func TestPrepareObjectForStorage(t *testing.T) {
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
encodedData, _, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
v, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
require.NoError(t, err)
newObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
newObject, _, err := s.codec.Decode(v.raw.Bytes(), nil, &dashv1.Dashboard{})
require.NoError(t, err)
obj, err := utils.MetaAccessor(newObject)
require.NoError(t, err)
@@ -208,9 +208,9 @@ func TestPrepareObjectForStorage(t *testing.T) {
require.NoError(t, err)
meta.SetDeprecatedInternalID(1) // nolint:staticcheck
encodedData, _, err := s.prepareObjectForStorage(ctx, obj)
v, err := s.prepareObjectForStorage(ctx, obj)
require.NoError(t, err)
newObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
newObject, _, err := s.codec.Decode(v.raw.Bytes(), nil, &dashv1.Dashboard{})
require.NoError(t, err)
meta, err = utils.MetaAccessor(newObject)
require.NoError(t, err)
@@ -225,14 +225,14 @@ func TestPrepareObjectForStorage(t *testing.T) {
require.NoError(t, err)
meta.SetAnnotation(utils.AnnoKeyGrantPermissions, "default")
encodedData, p, err := s.prepareObjectForStorage(ctx, obj)
v, err := s.prepareObjectForStorage(ctx, obj)
require.NoError(t, err)
newObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
newObject, _, err := s.codec.Decode(v.raw.Bytes(), nil, &dashv1.Dashboard{})
require.NoError(t, err)
meta, err = utils.MetaAccessor(newObject)
require.NoError(t, err)
require.Empty(t, meta.GetAnnotation(utils.AnnoKeyGrantPermissions))
require.Equal(t, p, "default")
require.Equal(t, v.grantPermissions, "default")
})
t.Run("calculate generation", func(t *testing.T) {
@@ -295,23 +295,56 @@ func TestPrepareObjectForStorage(t *testing.T) {
require.Equal(t, int64(1), out.GetGeneration()) // still 1
})
})
t.Run("should fail invalid input", func(t *testing.T) {
_, err := s.prepareObjectForStorage(context.Background(), &dashv1.Dashboard{})
require.Error(t, err)
require.Contains(t, err.Error(), "missing auth info")
_, err = s.prepareObjectForUpdate(context.Background(), &dashv1.Dashboard{}, &dashv1.Dashboard{})
require.Error(t, err)
require.Contains(t, err.Error(), "missing auth info")
_, err = s.prepareObjectForStorage(ctx, &dashv1.Dashboard{})
require.Error(t, err)
require.Contains(t, err.Error(), "missing name")
_, err = s.prepareObjectForUpdate(ctx, &dashv1.Dashboard{}, &dashv1.Dashboard{})
require.Error(t, err)
require.Contains(t, err.Error(), "updated object must have a name")
_, err = s.prepareObjectForUpdate(ctx, &dashv1.Dashboard{ObjectMeta: v1.ObjectMeta{
Name: "test-name",
}}, &dashv1.Dashboard{ObjectMeta: v1.ObjectMeta{
Name: "not-the-same-name",
}})
require.Error(t, err)
require.Contains(t, err.Error(), "name mismatch between")
_, err = s.prepareObjectForStorage(ctx, &dashv1.Dashboard{ObjectMeta: v1.ObjectMeta{
Name: "test-name",
ResourceVersion: "123", // RV must not be set
}})
require.Error(t, err)
require.Equal(t, storage.ErrResourceVersionSetOnCreate, err)
})
}
func getPreparedObject(t *testing.T, ctx context.Context, s *Storage, obj runtime.Object, old runtime.Object) utils.GrafanaMetaAccessor {
t.Helper()
var raw []byte
var v objectForStorage
var err error
if old == nil {
raw, _, err = s.prepareObjectForStorage(ctx, obj)
v, err = s.prepareObjectForStorage(ctx, obj)
} else {
raw, err = s.prepareObjectForUpdate(ctx, obj, old)
v, err = s.prepareObjectForUpdate(ctx, obj, old)
}
require.NoError(t, err)
out := &unstructured.Unstructured{}
err = out.UnmarshalJSON(raw)
err = out.UnmarshalJSON(v.raw.Bytes())
require.NoError(t, err)
meta, err := utils.MetaAccessor(out)
@@ -324,7 +357,7 @@ func TestPrepareLargeObjectForStorage(t *testing.T) {
node, err := snowflake.NewNode(rand.Int64N(1024))
require.NoError(t, err)
ctx := authtypes.WithAuthInfo(context.Background(), &identity.StaticRequester{UserID: 1, UserUID: "user-uid", Type: authtypes.TypeUser})
ctx := authlib.WithAuthInfo(context.Background(), &identity.StaticRequester{UserID: 1, UserUID: "user-uid", Type: authlib.TypeUser})
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
@@ -341,7 +374,7 @@ func TestPrepareLargeObjectForStorage(t *testing.T) {
},
}
_, _, err := f.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
_, err := f.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
require.Nil(t, err)
require.True(t, los.deconstructed)
})
@@ -359,7 +392,7 @@ func TestPrepareLargeObjectForStorage(t *testing.T) {
},
}
_, _, err := f.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
_, err := f.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
require.Nil(t, err)
require.False(t, los.deconstructed)
})