[Folder] Add general UID validation (#95549)

* Add general UID validation

* [REVIEW] group all folder validation rules into struct

* Fix test
This commit is contained in:
Leonor Oliveira 2024-10-29 16:09:28 +01:00 committed by GitHub
parent 3e1046ee2e
commit 55afbdc6be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 101 additions and 4 deletions

View File

@ -36,6 +36,14 @@ var resourceInfo = v0alpha1.FolderResourceInfo
var errNoUser = errors.New("valid user is required")
var errNoResource = errors.New("resource name is required")
var folderValidationRules = struct {
maxDepth int
invalidNames []string
}{
maxDepth: 4,
invalidNames: []string{"general"},
}
// This is used just so wire has something unique to return
type FolderAPIBuilder struct {
gv schema.GroupVersion
@ -183,10 +191,6 @@ func (b *FolderAPIBuilder) GetAuthorizer() authorizer.Authorizer {
})
}
func (b *FolderAPIBuilder) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
return nil
}
func authorizerFunc(ctx context.Context, attr authorizer.Attributes) (*authorizerParams, error) {
verb := attr.GetVerb()
name := attr.GetName()
@ -220,3 +224,38 @@ func authorizerFunc(ctx context.Context, attr authorizer.Attributes) (*authorize
}
return &authorizerParams{evaluator: eval, user: user}, nil
}
func (b *FolderAPIBuilder) Validate(ctx context.Context, a admission.Attributes, _ admission.ObjectInterfaces) error {
// name cannot be called "general"
id := a.GetName()
for _, invalidName := range folderValidationRules.invalidNames {
if id == invalidName {
return dashboards.ErrFolderInvalidUID
}
}
obj := a.GetObject()
for i := 0; i <= folderValidationRules.maxDepth+1; i++ {
parent := getParent(obj)
if parent == "" {
break
}
if i == folderValidationRules.maxDepth+1 {
return folder.ErrMaximumDepthReached
}
// TODO: investigate the best way to get a folder by name, considering validate is called before the storage layer
// parent, err := getFolderByNameSomehow()
// obj = parent
}
return nil
}
func getParent(o runtime.Object) string {
meta, err := utils.MetaAccessor(o)
if err != nil {
return ""
}
return meta.GetFolder()
}

View File

@ -6,6 +6,7 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/authz/zanzana"
"github.com/grafana/grafana/pkg/services/dashboards"
@ -13,6 +14,8 @@ import (
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/user"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authorization/authorizer"
)
@ -114,3 +117,58 @@ func TestFolderAPIBuilder_getAuthorizerFunc(t *testing.T) {
})
}
}
func TestFolderAPIBuilder_Validate(t *testing.T) {
type input struct {
obj *unstructured.Unstructured
name string
}
tests := []struct {
name string
input input
err error
}{
{
name: "should return error when nameis invalid",
input: input{
obj: &unstructured.Unstructured{
Object: map[string]interface{}{
"meta": map[string]interface{}{"name": folderValidationRules.invalidNames[0]},
},
},
name: folderValidationRules.invalidNames[0],
},
err: dashboards.ErrFolderInvalidUID,
},
}
b := &FolderAPIBuilder{
gv: resourceInfo.GroupVersion(),
features: nil,
namespacer: func(_ int64) string { return "123" },
folderSvc: foldertest.NewFakeService(),
accessControl: acimpl.ProvideAccessControl(featuremgmt.WithFeatures("nestedFolders"), zanzana.NewNoopClient()),
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := b.Validate(context.Background(), admission.NewAttributesRecord(
tt.input.obj,
nil,
v0alpha1.SchemeGroupVersion.WithKind("folder"),
"stacks-123",
tt.input.name,
v0alpha1.SchemeGroupVersion.WithResource("folders"),
"",
"create",
nil,
true,
&user.SignedInUser{},
), nil)
if tt.err != nil {
require.ErrorIs(t, err, tt.err)
return
}
})
}
}