From f3bb2105af87f63acf636d702f08d912f4d530c0 Mon Sep 17 00:00:00 2001 From: Ahmad Hamzh <85109141+ahmadhamzh@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:28:04 +0300 Subject: [PATCH] Allow non admin users to manage BSL (#6589) * allow other than admins to manage cbsl * add impersonated client * refactor code * improve errors * review comments --------- Co-authored-by: moelsayed --- modules/api/cmd/kubermatic-api/main.go | 2 +- modules/api/cmd/kubermatic-api/wrappers_ce.go | 2 +- modules/api/cmd/kubermatic-api/wrappers_ee.go | 4 +- .../clusterbackup/storage-location/handler.go | 61 +++++-- .../storage-location/provider.go | 157 ++++++++++++------ .../api/pkg/ee/cmd/kubermatic-api/wrappers.go | 4 +- .../v2/clusterbackup/storage-location/cbsl.go | 48 +----- .../storage-location/wrappers_ce.go | 5 + .../storage-location/wrappers_ee.go | 20 +-- modules/api/pkg/provider/types.go | 36 ++-- 10 files changed, 197 insertions(+), 142 deletions(-) diff --git a/modules/api/cmd/kubermatic-api/main.go b/modules/api/cmd/kubermatic-api/main.go index f26ae2b175..3d1c54f0f0 100644 --- a/modules/api/cmd/kubermatic-api/main.go +++ b/modules/api/cmd/kubermatic-api/main.go @@ -410,7 +410,7 @@ func createInitProviders(ctx context.Context, options serverRunOptions, masterCf featureGatesProvider := kubernetesprovider.NewFeatureGatesProvider(options.featureGates) - backupStorageProvider := backupStorageProviderFactory(client) + backupStorageProvider := backupStorageProviderFactory(defaultImpersonationClient.CreateImpersonatedClient, client) return providers{ sshKey: sshKeyProvider, diff --git a/modules/api/cmd/kubermatic-api/wrappers_ce.go b/modules/api/cmd/kubermatic-api/wrappers_ce.go index 22fcc058f3..9626745b25 100644 --- a/modules/api/cmd/kubermatic-api/wrappers_ce.go +++ b/modules/api/cmd/kubermatic-api/wrappers_ce.go @@ -48,6 +48,6 @@ func groupProjectBindingFactory(_ kubernetes.ImpersonationClient, _ ctrlruntimec return nil } -func backupStorageProviderFactory(_ ctrlruntimeclient.Client) provider.BackupStorageProvider { +func backupStorageProviderFactory(_ kubernetes.ImpersonationClient, _ ctrlruntimeclient.Client) provider.BackupStorageProvider { return nil } diff --git a/modules/api/cmd/kubermatic-api/wrappers_ee.go b/modules/api/cmd/kubermatic-api/wrappers_ee.go index dec6822ee7..219047cb5c 100644 --- a/modules/api/cmd/kubermatic-api/wrappers_ee.go +++ b/modules/api/cmd/kubermatic-api/wrappers_ee.go @@ -49,6 +49,6 @@ func groupProjectBindingFactory(createMasterImpersonatedClient kubernetes.Impers return eeapi.GroupProjectBindingProviderFactory(createMasterImpersonatedClient, privilegedClient) } -func backupStorageProviderFactory(privilegedClient ctrlruntimeclient.Client) provider.BackupStorageProvider { - return eeapi.BackupStorageProviderFactory(privilegedClient) +func backupStorageProviderFactory(createMasterImpersonatedClient kubernetes.ImpersonationClient, privilegedClient ctrlruntimeclient.Client) provider.BackupStorageProvider { + return eeapi.BackupStorageProviderFactory(createMasterImpersonatedClient, privilegedClient) } diff --git a/modules/api/pkg/ee/clusterbackup/storage-location/handler.go b/modules/api/pkg/ee/clusterbackup/storage-location/handler.go index b0bbe68963..f3a4f6005e 100644 --- a/modules/api/pkg/ee/clusterbackup/storage-location/handler.go +++ b/modules/api/pkg/ee/clusterbackup/storage-location/handler.go @@ -96,16 +96,23 @@ const ( displayNameLabelKey = "csbl-display-name" ) -func ListCBSL(ctx context.Context, request interface{}, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) ([]*apiv2.ClusterBackupStorageLocation, error) { +func ListCBSL(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) ([]*apiv2.ClusterBackupStorageLocation, error) { req, ok := request.(listCbslReq) if !ok { return nil, utilerrors.NewBadRequest("invalid request") } + + user, err := userInfoGetter(ctx, req.ProjectID) + + if err != nil { + return nil, err + } + labelSet := map[string]string{ kubermaticv1.ProjectIDLabelKey: req.ProjectID, } - cbslList, err := provider.ListUnsecured(ctx, labelSet) + cbslList, err := provider.ListUnsecured(ctx, user, labelSet) if err != nil { return nil, err } @@ -122,20 +129,31 @@ func ListCBSL(ctx context.Context, request interface{}, provider provider.Backup return resp, nil } -func GetCSBL(ctx context.Context, request interface{}, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { +func GetCSBL(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { req, ok := request.(getCbslReq) if !ok { return nil, utilerrors.NewBadRequest("invalid request") } + + user, err := userInfoGetter(ctx, req.ProjectID) + + if err != nil { + return nil, err + } + labelSet := map[string]string{ kubermaticv1.ProjectIDLabelKey: req.ProjectID, } - cbsl, err := provider.GetUnsecured(ctx, req.ClusterBackupStorageLocationName, labelSet) + cbsl, err := provider.GetUnsecured(ctx, user, req.ClusterBackupStorageLocationName, labelSet) if err != nil { return nil, err } + if cbsl == nil { + return nil, nil + } + return &apiv2.ClusterBackupStorageLocation{ Name: cbsl.Name, DisplayName: cbsl.Labels[displayNameLabelKey], @@ -144,18 +162,25 @@ func GetCSBL(ctx context.Context, request interface{}, provider provider.BackupS }, nil } -func CreateCBSL(ctx context.Context, request interface{}, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { +func CreateCBSL(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { req, ok := request.(createCbslReq) if !ok { return nil, utilerrors.NewBadRequest("invalid request") } + + user, err := userInfoGetter(ctx, req.ProjectID) + + if err != nil { + return nil, err + } + cbslName := req.Body.Name cbslSpec := req.Body.CBSLSpec.DeepCopy() creds := req.Body.Credentials cbsl := &kubermaticv1.ClusterBackupStorageLocation{ Spec: *cbslSpec, } - created, err := provider.CreateUnsecured(ctx, cbslName, req.ProjectID, cbsl, creds) + created, err := provider.Create(ctx, user, cbslName, req.ProjectID, cbsl, creds) if err != nil { return nil, err } @@ -168,13 +193,19 @@ func CreateCBSL(ctx context.Context, request interface{}, provider provider.Back }, nil } -func DeleteCBSL(ctx context.Context, request interface{}, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) error { +func DeleteCBSL(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) error { req, ok := request.(deleteCbslReq) if !ok { return utilerrors.NewBadRequest("invalid request") } - err := provider.DeleteUnsecured(ctx, req.ClusterBackupStorageLocationName) + user, err := userInfoGetter(ctx, req.ProjectID) + + if err != nil { + return err + } + + err = provider.Delete(ctx, user, req.ClusterBackupStorageLocationName) if err != nil { return err } @@ -182,17 +213,27 @@ func DeleteCBSL(ctx context.Context, request interface{}, provider provider.Back return nil } -func PatchCBSL(ctx context.Context, request interface{}, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { +func PatchCBSL(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { req, ok := request.(patchCbslReq) if !ok { return nil, utilerrors.NewBadRequest("invalid request") } + user, err := userInfoGetter(ctx, req.ProjectID) + + if err != nil { + return nil, err + } + cbslSpec := req.Body.CBSLSpec.DeepCopy() cbsl := &kubermaticv1.ClusterBackupStorageLocation{ Spec: *cbslSpec, } - patched, err := provider.PatchUnsecured(ctx, req.ClusterBackupStorageLocationName, cbsl, req.Body.Credentials) + patched, err := provider.Patch(ctx, user, req.ClusterBackupStorageLocationName, cbsl, req.Body.Credentials) + if err != nil { + return nil, err + } + if err != nil { return nil, err } diff --git a/modules/api/pkg/ee/clusterbackup/storage-location/provider.go b/modules/api/pkg/ee/clusterbackup/storage-location/provider.go index d9b7b64c34..2079977920 100644 --- a/modules/api/pkg/ee/clusterbackup/storage-location/provider.go +++ b/modules/api/pkg/ee/clusterbackup/storage-location/provider.go @@ -26,77 +26,108 @@ package storagelocation import ( "context" + "errors" "fmt" apiv2 "k8c.io/dashboard/v2/pkg/api/v2" + "k8c.io/dashboard/v2/pkg/handler/v1/common" "k8c.io/dashboard/v2/pkg/provider" + "k8c.io/dashboard/v2/pkg/provider/kubernetes" kubermaticv1 "k8c.io/kubermatic/v2/pkg/apis/kubermatic/v1" "k8c.io/kubermatic/v2/pkg/resources" + utilerrors "k8c.io/kubermatic/v2/pkg/util/errors" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" + "k8s.io/apiserver/pkg/storage/names" + restclient "k8s.io/client-go/rest" ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" ) type BackupStorageProvider struct { - privilegedClient ctrlruntimeclient.Client + createMasterImpersonatedClient kubernetes.ImpersonationClient + privilegedClient ctrlruntimeclient.Client } var _ provider.BackupStorageProvider = &BackupStorageProvider{} -func NewBackupStorageProvider(privilegedClient ctrlruntimeclient.Client) *BackupStorageProvider { +const ( + ownerReferenceApi = "kubermatic.k8s.io/v1" + ownerReferenceKind = "ClusterBackupStorageLocation" + credentialsSecretKeyName = "cloud-credentials" +) + +func NewBackupStorageProvider(createMasterImpersonatedClient kubernetes.ImpersonationClient, privilegedClient ctrlruntimeclient.Client) *BackupStorageProvider { return &BackupStorageProvider{ - privilegedClient: privilegedClient, + createMasterImpersonatedClient: createMasterImpersonatedClient, + privilegedClient: privilegedClient, } } -func (p *BackupStorageProvider) ListUnsecured(ctx context.Context, labelSet map[string]string) (*kubermaticv1.ClusterBackupStorageLocationList, error) { +func (p *BackupStorageProvider) ListUnsecured(ctx context.Context, userInfo *provider.UserInfo, labelSet map[string]string) (*kubermaticv1.ClusterBackupStorageLocationList, error) { + if userInfo == nil { + return nil, errors.New("a user is missing but required") + } + listOpts := &ctrlruntimeclient.ListOptions{ LabelSelector: labels.SelectorFromSet(labelSet), Namespace: resources.KubermaticNamespace, } cbslList := &kubermaticv1.ClusterBackupStorageLocationList{} + if err := p.privilegedClient.List(ctx, cbslList, listOpts); err != nil { return nil, fmt.Errorf("failed to list ClusterBackupStorageLocations: %w", err) } return cbslList, nil } -// func (p *BackupStorageProvider) Get(ctx context.Context, userInfo *provider.UserInfo, name string) (*kubermaticv1.ClusterBackupStorageLocation, error). -func (p *BackupStorageProvider) GetUnsecured(ctx context.Context, name string, labelSet map[string]string) (*kubermaticv1.ClusterBackupStorageLocation, error) { - // we use List() to filter CBSLs with labels easily, to keep scoped into the owner project. - cbslList, err := p.ListUnsecured(ctx, labelSet) +func (p *BackupStorageProvider) GetUnsecured(ctx context.Context, userInfo *provider.UserInfo, name string, labelSet map[string]string) (*kubermaticv1.ClusterBackupStorageLocation, error) { + if userInfo == nil { + return nil, errors.New("a user is missing but required") + } + + cbslList, err := p.ListUnsecured(ctx, userInfo, labelSet) if err != nil { - return nil, fmt.Errorf("failed to list ClusterBackupStorageLocations: %w", err) + return nil, err } for _, cbsl := range cbslList.Items { if cbsl.Name == name { return &cbsl, nil } } - return nil, nil + return nil, utilerrors.NewNotFound("ClusterBackupStorageLocation", name) } -func (p *BackupStorageProvider) CreateUnsecured(ctx context.Context, cbslName, projectID string, cbsl *kubermaticv1.ClusterBackupStorageLocation, credentials apiv2.S3BackupCredentials) (*kubermaticv1.ClusterBackupStorageLocation, error) { +func (p *BackupStorageProvider) Create(ctx context.Context, userInfo *provider.UserInfo, cbslName, projectID string, cbsl *kubermaticv1.ClusterBackupStorageLocation, credentials apiv2.S3BackupCredentials) (*kubermaticv1.ClusterBackupStorageLocation, error) { + if userInfo == nil { + return nil, errors.New("a user is missing but required") + } cbslFullName := fmt.Sprintf("%s-%s", cbslName, projectID) + secretName := fmt.Sprintf("credential-%s-", cbslFullName) secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - GenerateName: fmt.Sprintf("%s-", cbslFullName), - Namespace: resources.KubermaticNamespace, - Labels: getCSBLLabels(cbslName, projectID), + Name: names.SimpleNameGenerator.GenerateName(secretName), + Namespace: resources.KubermaticNamespace, + Labels: getCSBLLabels(cbslName, projectID), }, Data: map[string][]byte{ "accessKeyId": []byte(credentials.AccessKeyID), "secretAccessKey": []byte(credentials.SecretAccessKey), }, } - if err := p.privilegedClient.Create(ctx, secret); err != nil { - return nil, err + client := p.privilegedClient + + if !userInfo.IsAdmin { + var err error + client, err = p.getImpersonatedClient(userInfo) + if err != nil { + return nil, err + } } cbsl.ObjectMeta = metav1.ObjectMeta{ @@ -108,49 +139,70 @@ func (p *BackupStorageProvider) CreateUnsecured(ctx context.Context, cbslName, p LocalObjectReference: corev1.LocalObjectReference{ Name: secret.Name, }, - Key: "cloud-credentials", + Key: credentialsSecretKeyName, + } + if err := client.Create(ctx, cbsl); err != nil { + return nil, common.KubernetesErrorToHTTPError(err) } - if err := p.privilegedClient.Create(ctx, cbsl); err != nil { + ownerReference := metav1.OwnerReference{ + APIVersion: ownerReferenceApi, + Kind: ownerReferenceKind, + Name: cbslFullName, + UID: cbsl.UID, + } + + secret.ObjectMeta.OwnerReferences = append(secret.ObjectMeta.OwnerReferences, ownerReference) + + if err := p.privilegedClient.Create(ctx, secret); err != nil { return nil, err } return cbsl, nil } -func (p *BackupStorageProvider) DeleteUnsecured(ctx context.Context, name string) error { - cbsl := &kubermaticv1.ClusterBackupStorageLocation{} - if err := p.privilegedClient.Get(ctx, types.NamespacedName{Name: name, Namespace: resources.KubermaticNamespace}, cbsl); err != nil { - if apierrors.IsNotFound(err) { - return nil - } - return err +func (p *BackupStorageProvider) Delete(ctx context.Context, userInfo *provider.UserInfo, name string) error { + if userInfo == nil { + return errors.New("a user is missing but required") } - if cbsl.Spec.Credential != nil { - secretName := cbsl.Spec.Credential.Name - if err := p.deleteCredentials(ctx, secretName); err != nil { + client := p.privilegedClient + if !userInfo.IsAdmin { + var err error + client, err = p.getImpersonatedClient(userInfo) + if err != nil { return err } } - return p.privilegedClient.Delete(ctx, cbsl) -} -func (p *BackupStorageProvider) deleteCredentials(ctx context.Context, secretName string) error { - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: resources.KubermaticNamespace, - }, + cbsl := &kubermaticv1.ClusterBackupStorageLocation{} + if err := client.Get(ctx, types.NamespacedName{Name: name, Namespace: resources.KubermaticNamespace}, cbsl); err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return common.KubernetesErrorToHTTPError(err) } - if err := p.privilegedClient.Delete(ctx, secret); err != nil && !apierrors.IsNotFound(err) { - return err + if err := client.Delete(ctx, cbsl); err != nil { + return common.KubernetesErrorToHTTPError(err) } return nil } -func (p *BackupStorageProvider) PatchUnsecured(ctx context.Context, cbslName string, cbsl *kubermaticv1.ClusterBackupStorageLocation, updatedCreds apiv2.S3BackupCredentials) (*kubermaticv1.ClusterBackupStorageLocation, error) { +func (p *BackupStorageProvider) Patch(ctx context.Context, userInfo *provider.UserInfo, cbslName string, cbsl *kubermaticv1.ClusterBackupStorageLocation, updatedCreds apiv2.S3BackupCredentials) (*kubermaticv1.ClusterBackupStorageLocation, error) { + if userInfo == nil { + return nil, errors.New("a user is missing but required") + } + + client := p.privilegedClient + if !userInfo.IsAdmin { + var err error + client, err = p.getImpersonatedClient(userInfo) + if err != nil { + return nil, err + } + } + existing := &kubermaticv1.ClusterBackupStorageLocation{} - if err := p.privilegedClient.Get(ctx, types.NamespacedName{Name: cbslName, Namespace: resources.KubermaticNamespace}, existing); err != nil { - return nil, err + if err := client.Get(ctx, types.NamespacedName{Name: cbslName, Namespace: resources.KubermaticNamespace}, existing); err != nil { + return nil, common.KubernetesErrorToHTTPError(err) } if existing.Spec.Credential != nil && existing.Spec.Credential.Name == "" { @@ -158,9 +210,6 @@ func (p *BackupStorageProvider) PatchUnsecured(ctx context.Context, cbslName str } secretName := existing.Spec.Credential.Name - if err := p.patchCredentials(ctx, secretName, updatedCreds); err != nil { - return nil, err - } updated := existing.DeepCopy() updated.Spec = *cbsl.Spec.DeepCopy() @@ -169,10 +218,14 @@ func (p *BackupStorageProvider) PatchUnsecured(ctx context.Context, cbslName str LocalObjectReference: corev1.LocalObjectReference{ Name: secretName, }, - Key: "cloud-credentials", + Key: credentialsSecretKeyName, } - if err := p.privilegedClient.Patch(ctx, updated, ctrlruntimeclient.MergeFrom(existing)); err != nil { - return nil, err + if err := client.Patch(ctx, updated, ctrlruntimeclient.MergeFrom(existing)); err != nil { + return nil, common.KubernetesErrorToHTTPError(err) + } + + if err := p.patchCredentials(ctx, secretName, updatedCreds); err != nil { + return nil, common.KubernetesErrorToHTTPError(err) } return updated, nil } @@ -185,7 +238,7 @@ func (p *BackupStorageProvider) patchCredentials(ctx context.Context, secretName existing := &corev1.Secret{} if err := p.privilegedClient.Get(ctx, types.NamespacedName{Name: secretName, Namespace: resources.KubermaticNamespace}, existing); err != nil { - return err + return common.KubernetesErrorToHTTPError(err) } // same credentials if credentials.AccessKeyID == string(existing.Data["accessKeyId"]) && @@ -200,3 +253,11 @@ func (p *BackupStorageProvider) patchCredentials(ctx context.Context, secretName } return p.privilegedClient.Patch(ctx, updated, ctrlruntimeclient.MergeFrom(existing)) } + +func (p *BackupStorageProvider) getImpersonatedClient(userInfo *provider.UserInfo) (ctrlruntimeclient.Client, error) { + impersonationCfg := restclient.ImpersonationConfig{ + UserName: userInfo.Email, + Groups: userInfo.Groups, + } + return p.createMasterImpersonatedClient(impersonationCfg) +} diff --git a/modules/api/pkg/ee/cmd/kubermatic-api/wrappers.go b/modules/api/pkg/ee/cmd/kubermatic-api/wrappers.go index ce909f5b1d..3a02a4fc90 100644 --- a/modules/api/pkg/ee/cmd/kubermatic-api/wrappers.go +++ b/modules/api/pkg/ee/cmd/kubermatic-api/wrappers.go @@ -53,6 +53,6 @@ func GroupProjectBindingProviderFactory(createMasterImpersonatedClient kubernete return groupprojectbinding.NewGroupProjectBindingProvider(createMasterImpersonatedClient, privilegedClient) } -func BackupStorageProviderFactory(privilegedClient ctrlruntimeclient.Client) provider.BackupStorageProvider { - return backupstorage.NewBackupStorageProvider(privilegedClient) +func BackupStorageProviderFactory(createMasterImpersonatedClient kubernetes.ImpersonationClient, privilegedClient ctrlruntimeclient.Client) provider.BackupStorageProvider { + return backupstorage.NewBackupStorageProvider(createMasterImpersonatedClient, privilegedClient) } diff --git a/modules/api/pkg/handler/v2/clusterbackup/storage-location/cbsl.go b/modules/api/pkg/handler/v2/clusterbackup/storage-location/cbsl.go index e5814b757b..cab7864b18 100644 --- a/modules/api/pkg/handler/v2/clusterbackup/storage-location/cbsl.go +++ b/modules/api/pkg/handler/v2/clusterbackup/storage-location/cbsl.go @@ -18,76 +18,38 @@ package storagelocation import ( "context" - "fmt" - "net/http" "github.com/go-kit/kit/endpoint" "k8c.io/dashboard/v2/pkg/provider" - utilerrors "k8c.io/kubermatic/v2/pkg/util/errors" ) func ListCBSLEndpoint(userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) endpoint.Endpoint { return func(ctx context.Context, req interface{}) (interface{}, error) { - userInfo, err := userInfoGetter(ctx, "") - if err != nil { - return nil, err - } - if !userInfo.IsAdmin { - return nil, utilerrors.New(http.StatusForbidden, fmt.Sprintf("forbidden: \"%s\" doesn't have admin rights", userInfo.Email)) - } - return listCBSL(ctx, req, provider, projectProvider) + return listCBSL(ctx, req, userInfoGetter, provider, projectProvider) } } func GetCBSLEndpoint(userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) endpoint.Endpoint { return func(ctx context.Context, req interface{}) (interface{}, error) { - userInfo, err := userInfoGetter(ctx, "") - if err != nil { - return nil, err - } - if !userInfo.IsAdmin { - return nil, utilerrors.New(http.StatusForbidden, fmt.Sprintf("forbidden: \"%s\" doesn't have admin rights", userInfo.Email)) - } - return getCBSL(ctx, req, provider, projectProvider) + return getCBSL(ctx, req, userInfoGetter, provider, projectProvider) } } func CreateCBSLEndpoint(userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) endpoint.Endpoint { return func(ctx context.Context, req interface{}) (interface{}, error) { - userInfo, err := userInfoGetter(ctx, "") - if err != nil { - return nil, err - } - if !userInfo.IsAdmin { - return nil, utilerrors.New(http.StatusForbidden, fmt.Sprintf("forbidden: \"%s\" doesn't have admin rights", userInfo.Email)) - } - return createCBSL(ctx, req, provider, projectProvider) + return createCBSL(ctx, req, userInfoGetter, provider, projectProvider) } } func DeleteCBSLEndpoint(userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) endpoint.Endpoint { return func(ctx context.Context, req interface{}) (interface{}, error) { - userInfo, err := userInfoGetter(ctx, "") - if err != nil { - return nil, err - } - if !userInfo.IsAdmin { - return nil, utilerrors.New(http.StatusForbidden, fmt.Sprintf("forbidden: \"%s\" doesn't have admin rights", userInfo.Email)) - } - return nil, deleteCBSL(ctx, req, provider, projectProvider) + return nil, deleteCBSL(ctx, req, userInfoGetter, provider, projectProvider) } } func PatchCBSLEndpoint(userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) endpoint.Endpoint { return func(ctx context.Context, req interface{}) (interface{}, error) { - userInfo, err := userInfoGetter(ctx, "") - if err != nil { - return nil, err - } - if !userInfo.IsAdmin { - return nil, utilerrors.New(http.StatusForbidden, fmt.Sprintf("forbidden: \"%s\" doesn't have admin rights", userInfo.Email)) - } - return patchCBSL(ctx, req, provider, projectProvider) + return patchCBSL(ctx, req, userInfoGetter, provider, projectProvider) } } diff --git a/modules/api/pkg/handler/v2/clusterbackup/storage-location/wrappers_ce.go b/modules/api/pkg/handler/v2/clusterbackup/storage-location/wrappers_ce.go index 074d375434..5d3c98b16b 100644 --- a/modules/api/pkg/handler/v2/clusterbackup/storage-location/wrappers_ce.go +++ b/modules/api/pkg/handler/v2/clusterbackup/storage-location/wrappers_ce.go @@ -29,6 +29,7 @@ import ( func listCBSL( _ context.Context, _ interface{}, + _ provider.UserInfoGetter, _ provider.BackupStorageProvider, _ provider.ProjectProvider, ) ([]*apiv2.ClusterBackupStorageLocation, error) { @@ -38,6 +39,7 @@ func listCBSL( func getCBSL( _ context.Context, _ interface{}, + _ provider.UserInfoGetter, _ provider.BackupStorageProvider, _ provider.ProjectProvider, ) (*apiv2.ClusterBackupStorageLocation, error) { @@ -47,6 +49,7 @@ func getCBSL( func createCBSL( _ context.Context, _ interface{}, + _ provider.UserInfoGetter, _ provider.BackupStorageProvider, _ provider.ProjectProvider, ) (*apiv2.ClusterBackupStorageLocation, error) { @@ -56,6 +59,7 @@ func createCBSL( func deleteCBSL( _ context.Context, _ interface{}, + _ provider.UserInfoGetter, _ provider.BackupStorageProvider, _ provider.ProjectProvider, ) error { @@ -65,6 +69,7 @@ func deleteCBSL( func patchCBSL( _ context.Context, _ interface{}, + _ provider.UserInfoGetter, _ provider.BackupStorageProvider, _ provider.ProjectProvider, ) (*apiv2.ClusterBackupStorageLocation, error) { diff --git a/modules/api/pkg/handler/v2/clusterbackup/storage-location/wrappers_ee.go b/modules/api/pkg/handler/v2/clusterbackup/storage-location/wrappers_ee.go index c5585acfba..bbca52199d 100644 --- a/modules/api/pkg/handler/v2/clusterbackup/storage-location/wrappers_ee.go +++ b/modules/api/pkg/handler/v2/clusterbackup/storage-location/wrappers_ee.go @@ -27,24 +27,24 @@ import ( "k8c.io/dashboard/v2/pkg/provider" ) -func listCBSL(ctx context.Context, request interface{}, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) ([]*apiv2.ClusterBackupStorageLocation, error) { - return storagelocation.ListCBSL(ctx, request, provider, projectProvider) +func listCBSL(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) ([]*apiv2.ClusterBackupStorageLocation, error) { + return storagelocation.ListCBSL(ctx, request, userInfoGetter, provider, projectProvider) } -func getCBSL(ctx context.Context, request interface{}, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { - return storagelocation.GetCSBL(ctx, request, provider, projectProvider) +func getCBSL(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { + return storagelocation.GetCSBL(ctx, request, userInfoGetter, provider, projectProvider) } -func createCBSL(ctx context.Context, request interface{}, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { - return storagelocation.CreateCBSL(ctx, request, provider, projectProvider) +func createCBSL(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { + return storagelocation.CreateCBSL(ctx, request, userInfoGetter, provider, projectProvider) } -func deleteCBSL(ctx context.Context, request interface{}, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) error { - return storagelocation.DeleteCBSL(ctx, request, provider, projectProvider) +func deleteCBSL(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) error { + return storagelocation.DeleteCBSL(ctx, request, userInfoGetter, provider, projectProvider) } -func patchCBSL(ctx context.Context, request interface{}, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { - return storagelocation.PatchCBSL(ctx, request, provider, projectProvider) +func patchCBSL(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, provider provider.BackupStorageProvider, projectProvider provider.ProjectProvider) (*apiv2.ClusterBackupStorageLocation, error) { + return storagelocation.PatchCBSL(ctx, request, userInfoGetter, provider, projectProvider) } func DecodeListProjectCBSLReq(ctx context.Context, r *http.Request) (interface{}, error) { diff --git a/modules/api/pkg/provider/types.go b/modules/api/pkg/provider/types.go index d4c1469ac7..aec5b42f9d 100644 --- a/modules/api/pkg/provider/types.go +++ b/modules/api/pkg/provider/types.go @@ -1350,33 +1350,19 @@ type PrivilegedOperatingSystemProfileProvider interface { } type BackupStorageProvider interface { - // GetUnsecured returns a ClusterBackupStorageLocation based on object's name. - // - // Note that this function: - // is unsafe in a sense that it uses privileged account to get the resource - GetUnsecured(ctx context.Context, name string, labelSet map[string]string) (*kubermaticv1.ClusterBackupStorageLocation, error) - // ListUnsecured returns a ClusterBackupStorageLocation list. - // - // Note that this function: - // is unsafe in a sense that it uses privileged account to get the resource - ListUnsecured(ctx context.Context, labelSet map[string]string) (*kubermaticv1.ClusterBackupStorageLocationList, error) + // ListUnsecured returns a list of BackupStorageLocation for a given project. + ListUnsecured(ctx context.Context, userInfo *UserInfo, labelSet map[string]string) (*kubermaticv1.ClusterBackupStorageLocationList, error) - // CreateUnsecured creates a new ClusterBackupStorageLocation. - // - // Note that this function: - // is unsafe in a sense that it uses privileged account to update the resource - CreateUnsecured(ctx context.Context, cbslName, projectID string, cbsl *kubermaticv1.ClusterBackupStorageLocation, credentials apiv2.S3BackupCredentials) (*kubermaticv1.ClusterBackupStorageLocation, error) + // Get returns a BackupStorageLocation of a given name. + GetUnsecured(ctx context.Context, userInfo *UserInfo, name string, labelSet map[string]string) (*kubermaticv1.ClusterBackupStorageLocation, error) - // PatchUnsecured updates givenClusterBackupStorageLocation. - // - // Note that this function: - // is unsafe in a sense that it uses privileged account to update the resource - PatchUnsecured(ctx context.Context, cbslName string, updatedCBSL *kubermaticv1.ClusterBackupStorageLocation, UpdatedCreds apiv2.S3BackupCredentials) (*kubermaticv1.ClusterBackupStorageLocation, error) + // Create creates a new BackupStorageLocation. + Create(ctx context.Context, userInfo *UserInfo, cbslName, projectID string, cbsl *kubermaticv1.ClusterBackupStorageLocation, credentials apiv2.S3BackupCredentials) (*kubermaticv1.ClusterBackupStorageLocation, error) - // DeleteUnsecured removes an existing ClusterBackupStorageLocation. - // - // Note that this function: - // is unsafe in a sense that it uses privileged account to delete the resource - DeleteUnsecured(ctx context.Context, name string) error + // Delete removes an existing BackupStorageLocation. + Delete(ctx context.Context, userInfo *UserInfo, name string) error + + // Patch patches an existing GroupProjectBinding. + Patch(ctx context.Context, userInfo *UserInfo, cbslName string, updatedCBSL *kubermaticv1.ClusterBackupStorageLocation, UpdatedCreds apiv2.S3BackupCredentials) (*kubermaticv1.ClusterBackupStorageLocation, error) }