Files
grafana/pkg/registry/apis/dashboard/legacy_storage.go
Stephanie Hingtgen b887e8aa05 K8s: Dashboards: Add fine grained access control checks to /apis (#104347)
---------

Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
Co-authored-by: Gabriel MABILLE <gabriel.mabille@grafana.com>
Co-authored-by: Marco de Abreu <marco.deabreu@grafana.com>
Co-authored-by: Georges Chaudy <chaudyg@gmail.com>
2025-04-23 03:29:05 +01:00

113 lines
4.2 KiB
Go

package dashboard
import (
"context"
"github.com/prometheus/client_golang/prometheus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/utils"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/storage/unified/apistore"
"github.com/grafana/grafana/pkg/storage/unified/resource"
)
type DashboardStorage struct {
Access legacy.DashboardAccess
DashboardService dashboards.DashboardService
}
func (s *DashboardStorage) NewStore(dash utils.ResourceInfo, scheme *runtime.Scheme, defaultOptsGetter generic.RESTOptionsGetter, reg prometheus.Registerer, permissions dashboards.PermissionsRegistrationService, ac types.AccessClient) (grafanarest.Storage, error) {
server, err := resource.NewResourceServer(resource.ResourceServerOptions{
Backend: s.Access,
Reg: reg,
AccessClient: ac,
})
if err != nil {
return nil, err
}
defaultOpts, err := defaultOptsGetter.GetRESTOptions(dash.GroupResource(), nil)
if err != nil {
return nil, err
}
client := legacy.NewDirectResourceClient(server) // same context
optsGetter := apistore.NewRESTOptionsGetterForClient(client,
defaultOpts.StorageConfig.Config, nil,
)
optsGetter.RegisterOptions(dash.GroupResource(), apistore.StorageOptions{
EnableFolderSupport: true,
RequireDeprecatedInternalID: true,
Permissions: permissions.SetDefaultPermissionsAfterCreate,
})
store, err := grafanaregistry.NewRegistryStore(scheme, dash, optsGetter)
return &storeWrapper{
Store: store,
DashboardService: s.DashboardService,
}, err
}
type storeWrapper struct {
*registry.Store
DashboardService dashboards.DashboardService
}
// Create will create the dashboard using legacy storage and make sure the internal ID is set on the return object
func (s *storeWrapper) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
ctx = legacy.WithLegacyAccess(ctx)
obj, err := s.Store.Create(ctx, obj, createValidation, options)
access := legacy.GetLegacyAccess(ctx)
if access != nil && access.DashboardID > 0 {
meta, _ := utils.MetaAccessor(obj)
if meta != nil {
// skip the linter error for deprecated function
meta.SetDeprecatedInternalID(access.DashboardID) //nolint:staticcheck
}
}
meta, metaErr := utils.MetaAccessor(obj)
if metaErr == nil {
// Reconstruct the same UID as done at the storage level
// https://github.com/grafana/grafana/blob/a84e96fba29c3a1bb384fdbad1c9c658cc79ec8f/pkg/registry/apis/dashboard/legacy/sql_dashboards.go#L287
// This is necessary because the UID generated during the creation via legacy storage is actually never stored in the database
// and the one returned here is wrong.
meta.SetUID(gapiutil.CalculateClusterWideUID(obj))
}
if err != nil {
return obj, err
}
if metaErr != nil {
return obj, metaErr
}
return obj, nil
}
// Update will update the dashboard using legacy storage and make sure the internal ID is set on the return object
func (s *storeWrapper) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
ctx = legacy.WithLegacyAccess(ctx)
obj, created, err := s.Store.Update(ctx, name, objInfo, createValidation, updateValidation, forceAllowCreate, options)
access := legacy.GetLegacyAccess(ctx)
if access != nil && access.DashboardID > 0 {
meta, _ := utils.MetaAccessor(obj)
if meta != nil {
// skip the linter error for deprecated function
meta.SetDeprecatedInternalID(access.DashboardID) //nolint:staticcheck
}
}
return obj, created, err
}