Skip to content

Commit

Permalink
add impersonated client
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmadhamzh committed Mar 12, 2024
1 parent a6f9859 commit 6d591f8
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 63 deletions.
2 changes: 1 addition & 1 deletion modules/api/cmd/kubermatic-api/main.go
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion modules/api/cmd/kubermatic-api/wrappers_ce.go
Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions modules/api/cmd/kubermatic-api/wrappers_ee.go
Expand Up @@ -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)
}
26 changes: 13 additions & 13 deletions modules/api/pkg/ee/clusterbackup/storage-location/handler.go
Expand Up @@ -102,7 +102,7 @@ func ListCBSL(ctx context.Context, request interface{}, userInfoGetter provider.
return nil, utilerrors.NewBadRequest("invalid request")
}

_, err := userInfoGetter(ctx, req.ProjectID)
user, err := userInfoGetter(ctx, req.ProjectID)

if err != nil {
return nil, err
Expand All @@ -112,7 +112,7 @@ func ListCBSL(ctx context.Context, request interface{}, userInfoGetter provider.
kubermaticv1.ProjectIDLabelKey: req.ProjectID,
}

cbslList, err := provider.ListUnsecured(ctx, labelSet)
cbslList, err := provider.List(ctx, user, labelSet)
if err != nil {
return nil, err
}
Expand All @@ -135,7 +135,7 @@ func GetCSBL(ctx context.Context, request interface{}, userInfoGetter provider.U
return nil, utilerrors.NewBadRequest("invalid request")
}

_, err := userInfoGetter(ctx, req.ProjectID)
user, err := userInfoGetter(ctx, req.ProjectID)

if err != nil {
return nil, err
Expand All @@ -145,11 +145,15 @@ func GetCSBL(ctx context.Context, request interface{}, userInfoGetter provider.U
kubermaticv1.ProjectIDLabelKey: req.ProjectID,
}

cbsl, err := provider.GetUnsecured(ctx, req.ClusterBackupStorageLocationName, labelSet)
cbsl, err := provider.Get(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],
Expand All @@ -170,17 +174,13 @@ func CreateCBSL(ctx context.Context, request interface{}, userInfoGetter provide
return nil, err
}

if user.Roles.Has("viewers") {
return nil, fmt.Errorf("user with a viewer role is not permitted to create storage locations.")
}

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
}
Expand Down Expand Up @@ -209,7 +209,7 @@ func DeleteCBSL(ctx context.Context, request interface{}, userInfoGetter provide
return fmt.Errorf("user with a viewer role is not permitted to delete storage locations.")
}

err = provider.DeleteUnsecured(ctx, req.ClusterBackupStorageLocationName)
err = provider.Delete(ctx, user, req.ClusterBackupStorageLocationName)
if err != nil {
return err
}
Expand All @@ -223,17 +223,17 @@ func PatchCBSL(ctx context.Context, request interface{}, userInfoGetter provider
return nil, utilerrors.NewBadRequest("invalid request")
}

user, err := userInfoGetter(ctx, req.ProjectID)

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
}

user, err := userInfoGetter(ctx, req.ProjectID)

if err != nil {
return nil, err
}
Expand Down
104 changes: 85 additions & 19 deletions modules/api/pkg/ee/clusterbackup/storage-location/provider.go
Expand Up @@ -26,10 +26,12 @@ package storagelocation

import (
"context"
"errors"
"fmt"

apiv2 "k8c.io/dashboard/v2/pkg/api/v2"
"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"

Expand All @@ -38,40 +40,51 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
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 {
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) List(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 {
client := p.privilegedClient

if err := client.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) Get(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.List(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 {
Expand All @@ -81,7 +94,10 @@ func (p *BackupStorageProvider) GetUnsecured(ctx context.Context, name string, l
return nil, nil
}

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)

secret := &corev1.Secret{
Expand All @@ -95,7 +111,17 @@ func (p *BackupStorageProvider) CreateUnsecured(ctx context.Context, cbslName, p
"secretAccessKey": []byte(credentials.SecretAccessKey),
},
}
if err := p.privilegedClient.Create(ctx, secret); err != nil {
client := p.privilegedClient

if !userInfo.IsAdmin {
var err error
client, err = p.getImpersonatedClient(userInfo)
if err != nil {
return nil, err
}
}

if err := client.Create(ctx, secret); err != nil {
return nil, err
}

Expand All @@ -110,28 +136,43 @@ func (p *BackupStorageProvider) CreateUnsecured(ctx context.Context, cbslName, p
},
Key: "cloud-credentials",
}
if err := p.privilegedClient.Create(ctx, cbsl); err != nil {
if err := client.Create(ctx, cbsl); err != nil {
return nil, err
}
return cbsl, nil
}

func (p *BackupStorageProvider) DeleteUnsecured(ctx context.Context, name string) error {
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")
}

client := p.privilegedClient

cbsl := &kubermaticv1.ClusterBackupStorageLocation{}
if err := p.privilegedClient.Get(ctx, types.NamespacedName{Name: name, Namespace: resources.KubermaticNamespace}, cbsl); err != nil {
if err := client.Get(ctx, types.NamespacedName{Name: name, Namespace: resources.KubermaticNamespace}, cbsl); err != nil {
if apierrors.IsNotFound(err) {
return nil
}
return err
}

if !userInfo.IsAdmin {
var err error
client, err = p.getImpersonatedClient(userInfo)
if err != nil {
return err
}
}

if cbsl.Spec.Credential != nil {
secretName := cbsl.Spec.Credential.Name
if err := p.deleteCredentials(ctx, secretName); err != nil {
return err
}
}
return p.privilegedClient.Delete(ctx, cbsl)
return client.Delete(ctx, cbsl)

}

func (p *BackupStorageProvider) deleteCredentials(ctx context.Context, secretName string) error {
Expand All @@ -147,9 +188,23 @@ func (p *BackupStorageProvider) deleteCredentials(ctx context.Context, secretNam
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 {
if err := client.Get(ctx, types.NamespacedName{Name: cbslName, Namespace: resources.KubermaticNamespace}, existing); err != nil {
return nil, err
}

Expand All @@ -171,7 +226,7 @@ func (p *BackupStorageProvider) PatchUnsecured(ctx context.Context, cbslName str
},
Key: "cloud-credentials",
}
if err := p.privilegedClient.Patch(ctx, updated, ctrlruntimeclient.MergeFrom(existing)); err != nil {
if err := client.Patch(ctx, updated, ctrlruntimeclient.MergeFrom(existing)); err != nil {
return nil, err
}
return updated, nil
Expand Down Expand Up @@ -200,3 +255,14 @@ 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) {
if userInfo == nil {
return nil, errors.New("a user is missing but required")
}
impersonationCfg := restclient.ImpersonationConfig{
UserName: userInfo.Email,
Groups: userInfo.Groups,
}
return p.createMasterImpersonatedClient(impersonationCfg)
}
4 changes: 2 additions & 2 deletions modules/api/pkg/ee/cmd/kubermatic-api/wrappers.go
Expand Up @@ -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)
}
36 changes: 11 additions & 25 deletions modules/api/pkg/provider/types.go
Expand Up @@ -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)
// List returns a list of BackupStorageLocation for a given project.
List(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.
Get(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)
}

0 comments on commit 6d591f8

Please sign in to comment.