mirror of https://github.com/grafana/grafana.git
Folders: Create default permissions on root level folder in API Service and cleanup after delete (#111690)
This commit is contained in:
parent
ffe85d7c7e
commit
e083c05532
|
@ -16,7 +16,6 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
||||||
|
@ -112,6 +111,10 @@ func (s *folderStorage) Create(ctx context.Context,
|
||||||
|
|
||||||
parentUid := accessor.GetFolder()
|
parentUid := accessor.GetFolder()
|
||||||
|
|
||||||
|
// TODO: once the feature flag kubernetesAuthzResourcePermissionApis is removed AND the frontend is calling
|
||||||
|
// /apis directly (to set AnnoKeyGrantPermissions on root level folders), the below should be removed
|
||||||
|
// and we should instead initialize resourcePermissionsSvc in the RegisterAPIService function
|
||||||
|
// and rely on StorageOptions.Permissions.
|
||||||
err = s.setDefaultFolderPermissions(ctx, info.OrgID, user, p.Name, parentUid)
|
err = s.setDefaultFolderPermissions(ctx, info.OrgID, user, p.Name, parentUid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -133,22 +136,11 @@ func (s *folderStorage) Update(ctx context.Context,
|
||||||
|
|
||||||
// GracefulDeleter
|
// GracefulDeleter
|
||||||
func (s *folderStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
func (s *folderStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
obj, async, err := s.store.Delete(ctx, name, deleteValidation, options)
|
obj, async, err := s.store.Delete(ctx, name, deleteValidation, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return obj, async, err
|
return obj, async, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if accessErr := s.folderPermissionsSvc.DeleteResourcePermissions(ctx, info.OrgID, name); accessErr != nil {
|
|
||||||
// TODO: add a proper logger to this struct.
|
|
||||||
logger := log.New().FromContext(ctx)
|
|
||||||
logger.Warn("failed to delete folder permission after successfully deleting folder resource", "folder", name, "error", accessErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj, async, err
|
return obj, async, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +149,7 @@ func (s *folderStorage) DeleteCollection(ctx context.Context, deleteValidation r
|
||||||
return nil, fmt.Errorf("DeleteCollection for folders not implemented")
|
return nil, fmt.Errorf("DeleteCollection for folders not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *folderStorage) setDefaultFolderPermissions(ctx context.Context, orgID int64, user identity.Requester, uid string, parentUID string) error {
|
func (s *folderStorage) setDefaultFolderPermissions(ctx context.Context, orgID int64, user identity.Requester, uid, parentUID string) error {
|
||||||
var permissions []accesscontrol.SetResourcePermissionCommand
|
var permissions []accesscontrol.SetResourcePermissionCommand
|
||||||
|
|
||||||
if user.IsIdentityType(claims.TypeUser, claims.TypeServiceAccount) {
|
if user.IsIdentityType(claims.TypeUser, claims.TypeServiceAccount) {
|
||||||
|
|
|
@ -2,13 +2,18 @@ package folders
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apiserver/pkg/registry/generic/registry"
|
"k8s.io/apiserver/pkg/registry/generic/registry"
|
||||||
|
|
||||||
|
claims "github.com/grafana/authlib/types"
|
||||||
"github.com/grafana/grafana-app-sdk/logging"
|
"github.com/grafana/grafana-app-sdk/logging"
|
||||||
|
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||||
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// K8S docs say "Almost nobody should use this hook" about the "begin" hooks, but we do because we only need to
|
// K8S docs say "Almost nobody should use this hook" about the "begin" hooks, but we do because we only need to
|
||||||
|
@ -77,10 +82,35 @@ func (b *FolderAPIBuilder) afterDelete(obj runtime.Object, _ *metav1.DeleteOptio
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Propagating deleted folder to Zanzana", "folder", meta.GetName(), "parent", meta.GetFolder())
|
if b.features.IsEnabledGlobally(featuremgmt.FlagZanzana) {
|
||||||
err = b.permissionStore.DeleteFolderParents(ctx, meta.GetNamespace(), meta.GetName())
|
log.Info("Propagating deleted folder to Zanzana", "folder", meta.GetName(), "parent", meta.GetFolder())
|
||||||
if err != nil {
|
err = b.permissionStore.DeleteFolderParents(ctx, meta.GetNamespace(), meta.GetName())
|
||||||
log.Warn("failed to propagate folder to zanzana", "err", err)
|
if err != nil {
|
||||||
|
log.Warn("failed to propagate folder to zanzana", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.resourcePermissionsSvc != nil {
|
||||||
|
log.Debug("deleting folder permissions", "uid", meta.GetName(), "namespace", meta.GetNamespace())
|
||||||
|
client := (*b.resourcePermissionsSvc).Namespace(meta.GetNamespace())
|
||||||
|
err := client.Delete(ctx, fmt.Sprintf("%s-%s-%s", folders.FolderResourceInfo.GroupVersionResource().Group, folders.FolderResourceInfo.GroupVersionResource().Resource, meta.GetName()), metav1.DeleteOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to delete folder permissions", "error", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: once the feature flag kubernetesAuthzResourcePermissionApis is removed, we should initialize resourcePermissionsSvc
|
||||||
|
// in the RegisterAPIService function and the below should be removed
|
||||||
|
if !util.IsInterfaceNil(b.folderPermissionsSvc) {
|
||||||
|
ns, err := claims.ParseNamespace(meta.GetNamespace())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to parse namespace", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if accessErr := b.folderPermissionsSvc.DeleteResourcePermissions(ctx, ns.OrgID, meta.GetName()); accessErr != nil {
|
||||||
|
log.Warn("failed to delete folder permission after successfully deleting folder resource", "folder", meta.GetName(), "error", accessErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
@ -14,6 +15,7 @@ import (
|
||||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||||
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/kube-openapi/pkg/common"
|
"k8s.io/kube-openapi/pkg/common"
|
||||||
"k8s.io/kube-openapi/pkg/spec3"
|
"k8s.io/kube-openapi/pkg/spec3"
|
||||||
|
|
||||||
|
@ -22,8 +24,10 @@ import (
|
||||||
|
|
||||||
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||||
"github.com/grafana/grafana/apps/iam/pkg/reconcilers"
|
"github.com/grafana/grafana/apps/iam/pkg/reconcilers"
|
||||||
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||||
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
grafanaauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
|
grafanaauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
|
||||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||||
|
@ -54,10 +58,11 @@ type FolderAPIBuilder struct {
|
||||||
permissionsOnCreate bool
|
permissionsOnCreate bool
|
||||||
|
|
||||||
// Legacy services -- these will not exist in the MT environment
|
// Legacy services -- these will not exist in the MT environment
|
||||||
folderSvc folder.LegacyService
|
folderSvc folder.LegacyService
|
||||||
folderPermissionsSvc accesscontrol.FolderPermissionsService
|
resourcePermissionsSvc *dynamic.NamespaceableResourceInterface
|
||||||
acService accesscontrol.Service
|
folderPermissionsSvc accesscontrol.FolderPermissionsService // TODO: Remove this once kubernetesAuthzResourcePermissionApis is removed and the frontend is calling /apis directly to create root level folders
|
||||||
ac accesscontrol.AccessControl
|
acService accesscontrol.Service
|
||||||
|
ac accesscontrol.AccessControl
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterAPIService(cfg *setting.Cfg,
|
func RegisterAPIService(cfg *setting.Cfg,
|
||||||
|
@ -88,12 +93,13 @@ func RegisterAPIService(cfg *setting.Cfg,
|
||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAPIService(ac authlib.AccessClient, searcher resource.ResourceClient, features featuremgmt.FeatureToggles, zanzanaClient zanzana.Client) *FolderAPIBuilder {
|
func NewAPIService(ac authlib.AccessClient, searcher resource.ResourceClient, features featuremgmt.FeatureToggles, zanzanaClient zanzana.Client, resourcePermissionsSvc *dynamic.NamespaceableResourceInterface) *FolderAPIBuilder {
|
||||||
return &FolderAPIBuilder{
|
return &FolderAPIBuilder{
|
||||||
features: features,
|
features: features,
|
||||||
accessClient: ac,
|
accessClient: ac,
|
||||||
searcher: searcher,
|
searcher: searcher,
|
||||||
permissionStore: reconcilers.NewZanzanaPermissionStore(zanzanaClient),
|
permissionStore: reconcilers.NewZanzanaPermissionStore(zanzanaClient),
|
||||||
|
resourcePermissionsSvc: resourcePermissionsSvc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +144,9 @@ func (b *FolderAPIBuilder) AllowedV0Alpha1Resources() []string {
|
||||||
func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error {
|
func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error {
|
||||||
opts.StorageOptsRegister(resourceInfo.GroupResource(), apistore.StorageOptions{
|
opts.StorageOptsRegister(resourceInfo.GroupResource(), apistore.StorageOptions{
|
||||||
EnableFolderSupport: true,
|
EnableFolderSupport: true,
|
||||||
RequireDeprecatedInternalID: true})
|
RequireDeprecatedInternalID: true,
|
||||||
|
Permissions: b.setDefaultFolderPermissions,
|
||||||
|
})
|
||||||
|
|
||||||
unified, err := grafanaregistry.NewRegistryStore(opts.Scheme, resourceInfo, opts.OptsGetter)
|
unified, err := grafanaregistry.NewRegistryStore(opts.Scheme, resourceInfo, opts.OptsGetter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -193,17 +201,101 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultPermissions = []map[string]any{
|
||||||
|
{
|
||||||
|
"kind": "BasicRole",
|
||||||
|
"name": "Admin",
|
||||||
|
"verb": "admin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "BasicRole",
|
||||||
|
"name": "Editor",
|
||||||
|
"verb": "edit",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "BasicRole",
|
||||||
|
"name": "Viewer",
|
||||||
|
"verb": "view",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *FolderAPIBuilder) setDefaultFolderPermissions(ctx context.Context, key *resourcepb.ResourceKey, id authlib.AuthInfo, obj utils.GrafanaMetaAccessor) error {
|
||||||
|
if b.resourcePermissionsSvc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// only set default permissions for root folders
|
||||||
|
if obj.GetFolder() != "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log := logging.FromContext(ctx)
|
||||||
|
log.Debug("setting default folder permissions", "uid", obj.GetName(), "namespace", obj.GetNamespace())
|
||||||
|
|
||||||
|
client := (*b.resourcePermissionsSvc).Namespace(obj.GetNamespace())
|
||||||
|
name := fmt.Sprintf("%s-%s-%s", folders.FolderResourceInfo.GroupVersionResource().Group, folders.FolderResourceInfo.GroupVersionResource().Resource, obj.GetName())
|
||||||
|
|
||||||
|
// the resource permission will likely already exist with admin can admin, so we will need to update it
|
||||||
|
if _, err := client.Get(ctx, name, metav1.GetOptions{}); err == nil {
|
||||||
|
_, err := client.Update(ctx, &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"metadata": map[string]any{
|
||||||
|
"name": name,
|
||||||
|
"namespace": obj.GetNamespace(),
|
||||||
|
},
|
||||||
|
"spec": map[string]any{
|
||||||
|
"resource": map[string]any{
|
||||||
|
"apiGroup": folders.FolderResourceInfo.GroupVersionResource().Group,
|
||||||
|
"resource": folders.FolderResourceInfo.GroupVersionResource().Resource,
|
||||||
|
"name": obj.GetName(),
|
||||||
|
},
|
||||||
|
"permissions": defaultPermissions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, metav1.UpdateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to update root permissions", "error", err)
|
||||||
|
return fmt.Errorf("update root permissions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := client.Create(ctx, &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"metadata": map[string]any{
|
||||||
|
"name": name,
|
||||||
|
"namespace": obj.GetNamespace(),
|
||||||
|
},
|
||||||
|
"spec": map[string]any{
|
||||||
|
"resource": map[string]any{
|
||||||
|
"apiGroup": folders.FolderResourceInfo.GroupVersionResource().Group,
|
||||||
|
"resource": folders.FolderResourceInfo.GroupVersionResource().Resource,
|
||||||
|
"name": obj.GetName(),
|
||||||
|
},
|
||||||
|
"permissions": defaultPermissions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to create root permissions", "error", err)
|
||||||
|
return fmt.Errorf("create root permissions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *FolderAPIBuilder) registerPermissionHooks(store *genericregistry.Store) {
|
func (b *FolderAPIBuilder) registerPermissionHooks(store *genericregistry.Store) {
|
||||||
log := logging.FromContext(context.Background())
|
log := logging.FromContext(context.Background())
|
||||||
|
|
||||||
if b.features.IsEnabledGlobally(featuremgmt.FlagZanzana) {
|
if b.features.IsEnabledGlobally(featuremgmt.FlagZanzana) {
|
||||||
log.Info("Enabling Zanzana folder propagation hooks")
|
log.Info("Enabling Zanzana folder propagation hooks")
|
||||||
store.BeginCreate = b.beginCreate
|
store.BeginCreate = b.beginCreate
|
||||||
store.BeginUpdate = b.beginUpdate
|
store.BeginUpdate = b.beginUpdate
|
||||||
store.AfterDelete = b.afterDelete
|
|
||||||
} else {
|
} else {
|
||||||
log.Info("Zanzana is not enabled; skipping folder propagation hooks")
|
log.Info("Zanzana is not enabled; skipping folder propagation hooks")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
store.AfterDelete = b.afterDelete
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *FolderAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
|
func (b *FolderAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
|
||||||
|
|
|
@ -132,6 +132,8 @@ func (fm *FolderManager) EnsureFolderExists(ctx context.Context, folder Folder,
|
||||||
|
|
||||||
if parent != "" {
|
if parent != "" {
|
||||||
meta.SetFolder(parent)
|
meta.SetFolder(parent)
|
||||||
|
} else {
|
||||||
|
meta.SetAnnotation(utils.AnnoKeyGrantPermissions, utils.AnnoGrantPermissionsDefault)
|
||||||
}
|
}
|
||||||
meta.SetManagerProperties(utils.ManagerProperties{
|
meta.SetManagerProperties(utils.ManagerProperties{
|
||||||
Kind: utils.ManagerKindRepo,
|
Kind: utils.ManagerKindRepo,
|
||||||
|
|
|
@ -34,17 +34,14 @@ func afterCreatePermissionCreator(ctx context.Context,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if val.GetAnnotation(utils.AnnoKeyManagerKind) != "" {
|
|
||||||
return nil, fmt.Errorf("managed resource may not grant permissions")
|
|
||||||
}
|
|
||||||
auth, ok := authtypes.AuthInfoFrom(ctx)
|
auth, ok := authtypes.AuthInfoFrom(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("missing auth info")
|
return nil, errors.New("missing auth info")
|
||||||
}
|
}
|
||||||
|
|
||||||
idtype := auth.GetIdentityType()
|
idtype := auth.GetIdentityType()
|
||||||
if idtype != authtypes.TypeUser && idtype != authtypes.TypeServiceAccount {
|
if idtype != authtypes.TypeUser && idtype != authtypes.TypeServiceAccount && idtype != authtypes.TypeAccessPolicy {
|
||||||
return nil, fmt.Errorf("only users or service accounts may grant themselves permissions using an annotation")
|
return nil, fmt.Errorf("only users, service accounts, and access policies may grant permissions using an annotation")
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(ctx context.Context) error {
|
return func(ctx context.Context) error {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
|
|
||||||
authtypes "github.com/grafana/authlib/types"
|
authtypes "github.com/grafana/authlib/types"
|
||||||
|
|
||||||
|
@ -40,20 +39,6 @@ func TestAfterCreatePermissionCreator(t *testing.T) {
|
||||||
require.Contains(t, err.Error(), "missing default permission creator")
|
require.Contains(t, err.Error(), "missing default permission creator")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should error for managed resources", func(t *testing.T) {
|
|
||||||
obj := &v0alpha1.Dashboard{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Annotations: map[string]string{
|
|
||||||
utils.AnnoKeyManagerKind: "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
creator, err := afterCreatePermissionCreator(context.Background(), nil, utils.AnnoGrantPermissionsDefault, obj, mockSetter)
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Nil(t, creator)
|
|
||||||
require.Contains(t, err.Error(), "managed resource may not grant permissions")
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("should error when auth info is missing", func(t *testing.T) {
|
t.Run("should error when auth info is missing", func(t *testing.T) {
|
||||||
obj := &v0alpha1.Dashboard{}
|
obj := &v0alpha1.Dashboard{}
|
||||||
creator, err := afterCreatePermissionCreator(context.Background(), nil, utils.AnnoGrantPermissionsDefault, obj, mockSetter)
|
creator, err := afterCreatePermissionCreator(context.Background(), nil, utils.AnnoGrantPermissionsDefault, obj, mockSetter)
|
||||||
|
@ -108,6 +93,29 @@ func TestAfterCreatePermissionCreator(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("should succeed for access policy identity", func(t *testing.T) {
|
||||||
|
ctx := identity.WithRequester(context.Background(), &identity.StaticRequester{
|
||||||
|
Type: authtypes.TypeAccessPolicy,
|
||||||
|
OrgID: 1,
|
||||||
|
OrgRole: "Admin",
|
||||||
|
UserID: 1,
|
||||||
|
})
|
||||||
|
obj := &v0alpha1.Dashboard{}
|
||||||
|
key := &resourcepb.ResourceKey{
|
||||||
|
Group: "test",
|
||||||
|
Resource: "test",
|
||||||
|
Namespace: "test",
|
||||||
|
Name: "test",
|
||||||
|
}
|
||||||
|
|
||||||
|
creator, err := afterCreatePermissionCreator(ctx, key, utils.AnnoGrantPermissionsDefault, obj, mockSetter)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, creator)
|
||||||
|
|
||||||
|
err = creator(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("should error for non-user/non-service-account identity", func(t *testing.T) {
|
t.Run("should error for non-user/non-service-account identity", func(t *testing.T) {
|
||||||
ctx := identity.WithRequester(context.Background(), &identity.StaticRequester{
|
ctx := identity.WithRequester(context.Background(), &identity.StaticRequester{
|
||||||
Type: authtypes.TypeAnonymous,
|
Type: authtypes.TypeAnonymous,
|
||||||
|
@ -117,6 +125,6 @@ func TestAfterCreatePermissionCreator(t *testing.T) {
|
||||||
creator, err := afterCreatePermissionCreator(ctx, nil, utils.AnnoGrantPermissionsDefault, obj, mockSetter)
|
creator, err := afterCreatePermissionCreator(ctx, nil, utils.AnnoGrantPermissionsDefault, obj, mockSetter)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Nil(t, creator)
|
require.Nil(t, creator)
|
||||||
require.Contains(t, err.Error(), "only users or service accounts may grant themselves permissions")
|
require.Contains(t, err.Error(), "only users, service accounts, and access policies may grant permissions")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue