@@ -7,13 +7,15 @@ import (
77
88 "github.com/prometheus/client_golang/prometheus"
99 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1011 "k8s.io/apimachinery/pkg/runtime"
1112 "k8s.io/apimachinery/pkg/runtime/schema"
1213 "k8s.io/apiserver/pkg/admission"
1314 "k8s.io/apiserver/pkg/authorization/authorizer"
1415 genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
1516 "k8s.io/apiserver/pkg/registry/rest"
1617 genericapiserver "k8s.io/apiserver/pkg/server"
18+ "k8s.io/client-go/dynamic"
1719 "k8s.io/kube-openapi/pkg/common"
1820 "k8s.io/kube-openapi/pkg/spec3"
1921
@@ -22,8 +24,10 @@ import (
2224
2325 folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
2426 "github.com/grafana/grafana/apps/iam/pkg/reconcilers"
27+ "github.com/grafana/grafana/pkg/apimachinery/utils"
2528 grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
2629 grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
30+ "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
2731 "github.com/grafana/grafana/pkg/services/accesscontrol"
2832 grafanaauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
2933 "github.com/grafana/grafana/pkg/services/apiserver/builder"
@@ -54,10 +58,11 @@ type FolderAPIBuilder struct {
5458 permissionsOnCreate bool
5559
5660 // Legacy services -- these will not exist in the MT environment
57- folderSvc folder.LegacyService
58- folderPermissionsSvc accesscontrol.FolderPermissionsService
59- acService accesscontrol.Service
60- ac accesscontrol.AccessControl
61+ folderSvc folder.LegacyService
62+ resourcePermissionsSvc * dynamic.NamespaceableResourceInterface
63+ folderPermissionsSvc accesscontrol.FolderPermissionsService // TODO: Remove this once kubernetesAuthzResourcePermissionApis is removed and the frontend is calling /apis directly to create root level folders
64+ acService accesscontrol.Service
65+ ac accesscontrol.AccessControl
6166}
6267
6368func RegisterAPIService (cfg * setting.Cfg ,
@@ -88,12 +93,13 @@ func RegisterAPIService(cfg *setting.Cfg,
8893 return builder
8994}
9095
91- func NewAPIService (ac authlib.AccessClient , searcher resource.ResourceClient , features featuremgmt.FeatureToggles , zanzanaClient zanzana.Client ) * FolderAPIBuilder {
96+ func NewAPIService (ac authlib.AccessClient , searcher resource.ResourceClient , features featuremgmt.FeatureToggles , zanzanaClient zanzana.Client , resourcePermissionsSvc * dynamic. NamespaceableResourceInterface ) * FolderAPIBuilder {
9297 return & FolderAPIBuilder {
93- features : features ,
94- accessClient : ac ,
95- searcher : searcher ,
96- permissionStore : reconcilers .NewZanzanaPermissionStore (zanzanaClient ),
98+ features : features ,
99+ accessClient : ac ,
100+ searcher : searcher ,
101+ permissionStore : reconcilers .NewZanzanaPermissionStore (zanzanaClient ),
102+ resourcePermissionsSvc : resourcePermissionsSvc ,
97103 }
98104}
99105
@@ -138,7 +144,9 @@ func (b *FolderAPIBuilder) AllowedV0Alpha1Resources() []string {
138144func (b * FolderAPIBuilder ) UpdateAPIGroupInfo (apiGroupInfo * genericapiserver.APIGroupInfo , opts builder.APIGroupOptions ) error {
139145 opts .StorageOptsRegister (resourceInfo .GroupResource (), apistore.StorageOptions {
140146 EnableFolderSupport : true ,
141- RequireDeprecatedInternalID : true })
147+ RequireDeprecatedInternalID : true ,
148+ Permissions : b .setDefaultFolderPermissions ,
149+ })
142150
143151 unified , err := grafanaregistry .NewRegistryStore (opts .Scheme , resourceInfo , opts .OptsGetter )
144152 if err != nil {
@@ -193,17 +201,101 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
193201 return nil
194202}
195203
204+ var defaultPermissions = []map [string ]any {
205+ {
206+ "kind" : "BasicRole" ,
207+ "name" : "Admin" ,
208+ "verb" : "admin" ,
209+ },
210+ {
211+ "kind" : "BasicRole" ,
212+ "name" : "Editor" ,
213+ "verb" : "edit" ,
214+ },
215+ {
216+ "kind" : "BasicRole" ,
217+ "name" : "Viewer" ,
218+ "verb" : "view" ,
219+ },
220+ }
221+
222+ func (b * FolderAPIBuilder ) setDefaultFolderPermissions (ctx context.Context , key * resourcepb.ResourceKey , id authlib.AuthInfo , obj utils.GrafanaMetaAccessor ) error {
223+ if b .resourcePermissionsSvc == nil {
224+ return nil
225+ }
226+
227+ // only set default permissions for root folders
228+ if obj .GetFolder () != "" {
229+ return nil
230+ }
231+
232+ log := logging .FromContext (ctx )
233+ log .Debug ("setting default folder permissions" , "uid" , obj .GetName (), "namespace" , obj .GetNamespace ())
234+
235+ client := (* b .resourcePermissionsSvc ).Namespace (obj .GetNamespace ())
236+ name := fmt .Sprintf ("%s-%s-%s" , folders .FolderResourceInfo .GroupVersionResource ().Group , folders .FolderResourceInfo .GroupVersionResource ().Resource , obj .GetName ())
237+
238+ // the resource permission will likely already exist with admin can admin, so we will need to update it
239+ if _ , err := client .Get (ctx , name , metav1.GetOptions {}); err == nil {
240+ _ , err := client .Update (ctx , & unstructured.Unstructured {
241+ Object : map [string ]interface {}{
242+ "metadata" : map [string ]any {
243+ "name" : name ,
244+ "namespace" : obj .GetNamespace (),
245+ },
246+ "spec" : map [string ]any {
247+ "resource" : map [string ]any {
248+ "apiGroup" : folders .FolderResourceInfo .GroupVersionResource ().Group ,
249+ "resource" : folders .FolderResourceInfo .GroupVersionResource ().Resource ,
250+ "name" : obj .GetName (),
251+ },
252+ "permissions" : defaultPermissions ,
253+ },
254+ },
255+ }, metav1.UpdateOptions {})
256+ if err != nil {
257+ logger .Error ("failed to update root permissions" , "error" , err )
258+ return fmt .Errorf ("update root permissions: %w" , err )
259+ }
260+
261+ return nil
262+ }
263+
264+ _ , err := client .Create (ctx , & unstructured.Unstructured {
265+ Object : map [string ]interface {}{
266+ "metadata" : map [string ]any {
267+ "name" : name ,
268+ "namespace" : obj .GetNamespace (),
269+ },
270+ "spec" : map [string ]any {
271+ "resource" : map [string ]any {
272+ "apiGroup" : folders .FolderResourceInfo .GroupVersionResource ().Group ,
273+ "resource" : folders .FolderResourceInfo .GroupVersionResource ().Resource ,
274+ "name" : obj .GetName (),
275+ },
276+ "permissions" : defaultPermissions ,
277+ },
278+ },
279+ }, metav1.CreateOptions {})
280+ if err != nil {
281+ logger .Error ("failed to create root permissions" , "error" , err )
282+ return fmt .Errorf ("create root permissions: %w" , err )
283+ }
284+
285+ return nil
286+ }
287+
196288func (b * FolderAPIBuilder ) registerPermissionHooks (store * genericregistry.Store ) {
197289 log := logging .FromContext (context .Background ())
198-
199290 if b .features .IsEnabledGlobally (featuremgmt .FlagZanzana ) {
200291 log .Info ("Enabling Zanzana folder propagation hooks" )
201292 store .BeginCreate = b .beginCreate
202293 store .BeginUpdate = b .beginUpdate
203- store .AfterDelete = b .afterDelete
204294 } else {
205295 log .Info ("Zanzana is not enabled; skipping folder propagation hooks" )
206296 }
297+
298+ store .AfterDelete = b .afterDelete
207299}
208300
209301func (b * FolderAPIBuilder ) GetOpenAPIDefinitions () common.GetOpenAPIDefinitions {
0 commit comments