fix: use multi client for provisioning API builder (#111144)

This commit is contained in:
Charandas
2025-09-18 16:35:40 -07:00
committed by GitHub
parent 1d6c1da94f
commit 76b3d855c7
2 changed files with 41 additions and 18 deletions
+9 -1
View File
@@ -28,6 +28,7 @@ import (
authlib "github.com/grafana/authlib/types"
"github.com/grafana/grafana-app-sdk/logging"
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
@@ -132,8 +133,14 @@ func NewAPIBuilder(
extraWorkers []jobs.Worker,
jobHistoryConfig *JobHistoryConfig,
allowedTargets []provisioning.SyncTargetType,
newStandaloneClientFactoryFunc func(loopbackConfigProvider apiserver.RestConfigProvider) resources.ClientFactory, // optional, only used for standalone apiserver
) *APIBuilder {
clients := resources.NewClientFactory(configProvider)
var clients resources.ClientFactory
if newStandaloneClientFactoryFunc != nil {
clients = newStandaloneClientFactoryFunc(configProvider)
} else {
clients = resources.NewClientFactory(configProvider)
}
parsers := resources.NewParserFactory(clients)
resourceLister := resources.NewResourceListerForMigrations(unified, legacyMigrator, storageStatus)
@@ -237,6 +244,7 @@ func RegisterAPIService(
extraWorkers,
createJobHistoryConfigFromSettings(cfg),
allowedTargets,
nil,
)
apiregistration.RegisterAPI(builder)
return builder, nil
@@ -18,7 +18,14 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/utils"
)
// DualReadWriter is a wrapper around a repository that can read and write resources
// DualReadWriter is a wrapper around a repository that can read from and write resources
// into both the Git repository as well as in Grafana. It isn't a dual writer in the sense of what unistore handling calls dual writing.
// Standard provisioning Authorizer has already run by the time DualReadWriter is called
// for incoming requests from actors, external or internal. However, since it is the files
// connector that redirects here, the external resources such as dashboards
// end up requiring additional authorization checks which the DualReadWriter performs here.
// TODO: it does not support folders yet
type DualReadWriter struct {
repo repository.ReaderWriter
@@ -496,24 +503,32 @@ func (r *DualReadWriter) authorize(ctx context.Context, parsed *ParsedResource,
return apierrors.NewUnauthorized(err.Error())
}
// Use configured permissions for get+delete
if parsed.Existing != nil && (verb == utils.VerbGet || verb == utils.VerbDelete) {
rsp, err := r.access.Check(ctx, id, authlib.CheckRequest{
Group: parsed.GVR.Group,
Resource: parsed.GVR.Resource,
Namespace: parsed.Existing.GetNamespace(),
Name: parsed.Existing.GetName(),
Folder: parsed.Meta.GetFolder(),
Verb: utils.VerbGet,
})
if err != nil || !rsp.Allowed {
return apierrors.NewForbidden(parsed.GVR.GroupResource(), parsed.Obj.GetName(),
fmt.Errorf("no access to read the embedded file"))
}
var name string
if parsed.Existing != nil {
name = parsed.Existing.GetName()
} else {
name = parsed.Obj.GetName()
}
// Simple role based access for now
if id.GetOrgRole().Includes(identity.RoleEditor) {
rsp, err := r.access.Check(ctx, id, authlib.CheckRequest{
Group: parsed.GVR.Group,
Resource: parsed.GVR.Resource,
Namespace: id.GetNamespace(),
Name: name,
Folder: parsed.Meta.GetFolder(),
Verb: verb,
})
if err != nil || !rsp.Allowed {
return apierrors.NewForbidden(parsed.GVR.GroupResource(), parsed.Obj.GetName(),
fmt.Errorf("no access to read the embedded file"))
}
idType, _, err := authlib.ParseTypeID(id.GetID())
if err != nil {
return apierrors.NewForbidden(parsed.GVR.GroupResource(), parsed.Obj.GetName(), fmt.Errorf("could not determine identity type to check access"))
}
// only apply role based access if identity is not of type access policy
if idType == authlib.TypeAccessPolicy || id.GetOrgRole().Includes(identity.RoleEditor) {
return nil
}