...

Source file src/edge-infra.dev/pkg/edge/api/graph/resolver/organization_queries.resolvers.go

Documentation: edge-infra.dev/pkg/edge/api/graph/resolver

     1  package resolver
     2  
     3  // This file will be automatically regenerated based on the schema, any resolver implementations
     4  // will be copied through when generating and any unknown code will be moved to the end.
     5  // Code generated by github.com/99designs/gqlgen version v0.17.45
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"slices"
    12  	"strings"
    13  
    14  	"edge-infra.dev/pkg/edge/api/apierror"
    15  	"edge-infra.dev/pkg/edge/api/graph/mapper"
    16  	"edge-infra.dev/pkg/edge/api/graph/model"
    17  	"edge-infra.dev/pkg/edge/api/services"
    18  	"edge-infra.dev/pkg/edge/api/utils"
    19  	chariotClientApi "edge-infra.dev/pkg/edge/chariot/client"
    20  	"edge-infra.dev/pkg/edge/constants/api/banner"
    21  	"edge-infra.dev/pkg/edge/constants/api/cluster"
    22  	"edge-infra.dev/pkg/edge/constants/api/fleet"
    23  	"github.com/google/uuid"
    24  	"github.com/rs/zerolog/log"
    25  )
    26  
    27  // CreateBanner is the resolver for the createBanner field.
    28  func (r *mutationResolver) CreateBanner(ctx context.Context, name string, description *string, isOrgBanner *bool, bslID *string) (*model.EdgeResponsePayload, error) {
    29  	var (
    30  		bannerUUID = uuid.NewString()
    31  		projectID  = utils.GenerateProjectID()
    32  		b          *model.Banner
    33  		err        error
    34  		bannerType = utils.GetBannerType(isOrgBanner)
    35  		//organization = bsl.GetOrgShortName(middleware.ForContext(ctx).Organization)
    36  	)
    37  	path, err := r.BannerService.GetBannerInfraBucketPath(ctx)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	switch bannerType {
    42  	case banner.Org:
    43  		if utils.IsNullOrEmpty(bslID) {
    44  			b, err = r.BannerService.CreateOrganizationBanner(ctx, name, description)
    45  		} else {
    46  			existingBanner, errs := r.BannerService.GetBSLOrganization(ctx, *bslID)
    47  			if errs != nil {
    48  				return nil, errs
    49  			}
    50  			b = mapper.BSLOrgToBanner(existingBanner)
    51  			if strings.Contains(b.Name, "deleted") {
    52  				return nil, fmt.Errorf("bsl subOrg %s is deleted", *bslID)
    53  			}
    54  			if utils.IsNullOrEmpty(description) {
    55  				description = &existingBanner.Description
    56  			}
    57  		}
    58  	case banner.EU:
    59  		if utils.IsNullOrEmpty(bslID) {
    60  			b, err = r.BannerService.CreateEUBanner(ctx, name, description)
    61  		} else {
    62  			existingBanner, errs := r.BannerService.GetBSLEU(ctx, *bslID)
    63  			if errs != nil {
    64  				return nil, errs
    65  			}
    66  			b = mapper.EnterpriseUnitToBanner(existingBanner)
    67  			if strings.Contains(b.Name, "deleted") || !existingBanner.Active {
    68  				return nil, fmt.Errorf("enterprise unit %s is deleted", *bslID)
    69  			}
    70  			if utils.IsNullOrEmpty(description) {
    71  				description = &existingBanner.Description
    72  			}
    73  		}
    74  	}
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	errs := r.BannerService.CreateBannerSQLEntry(ctx, projectID, name, string(bannerType), b.BannerBSLId, bannerUUID, *description)
    80  	if errs != nil {
    81  		err := fmt.Errorf("error creating banner entry in sql: %w", errs)
    82  		undoErr := r.BannerService.UndoCreateBanner(ctx, name, b.BannerBSLId, bannerUUID, "", isOrgBanner, path)
    83  		if undoErr != nil {
    84  			err = fmt.Errorf("%w; (rollback errors: %s)", err, undoErr)
    85  		}
    86  		return nil, err
    87  	}
    88  
    89  	bannerObject, bannerCRErr := r.BannerService.CreateBannerCr(ctx, bannerUUID, projectID, name, b.BannerBSLId, make([]string, 0))
    90  	if bannerCRErr != nil {
    91  		undoErr := r.BannerService.UndoCreateBanner(ctx, name, b.BannerBSLId, bannerUUID, "", isOrgBanner, path)
    92  		if undoErr != nil {
    93  			bannerCRErr = fmt.Errorf("%w; (rollback errors: %s)", bannerCRErr, undoErr)
    94  		}
    95  		return nil, bannerCRErr
    96  	}
    97  	createStoreClusterMessage := chariotClientApi.
    98  		NewChariotMessage().
    99  		SetOperation(chariotClientApi.Create).
   100  		SetOwner(services.ComponentOwner).
   101  		SetBanner(r.Config.Bff.TopLevelProjectID).
   102  		SetCluster(path).
   103  		AddObject(bannerObject)
   104  
   105  	if err := r.ChariotService.InvokeChariotPubsub(ctx, createStoreClusterMessage, nil); err != nil {
   106  		err := fmt.Errorf("error calling chariot v2 and creating a banner cr: %w", err)
   107  		log.Ctx(ctx).Err(err).Msg("chariot invocation failed")
   108  		undoErr := r.BannerService.UndoCreateBanner(ctx, name, b.BannerBSLId, bannerUUID, "", isOrgBanner, path)
   109  		if undoErr != nil {
   110  			err = fmt.Errorf("%w; (rollback errors: %s)", err, undoErr)
   111  		}
   112  		return nil, err
   113  	}
   114  
   115  	return mapper.ToEdgeResponsePayload(200, "Successfully created "+b.Name), nil
   116  }
   117  
   118  // UpdateBanner is the resolver for the updateBanner field.
   119  func (r *mutationResolver) UpdateBanner(ctx context.Context, bannerEdgeID string, displayName *string, description *string, edgeSecurityCompliance *model.EdgeSecurityComplianceOptions) (*model.EdgeResponsePayload, error) {
   120  	bannerToUpdate, err := r.BannerService.GetBanner(ctx, bannerEdgeID)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	if displayName != nil {
   125  		bannerToUpdate.Name = *displayName
   126  	}
   127  	if description != nil {
   128  		bannerToUpdate.Description = description
   129  	}
   130  	var errs error
   131  	if banner.Type(bannerToUpdate.BannerType) == banner.EU {
   132  		errs = r.BannerService.UpdateEUBanner(ctx, bannerToUpdate)
   133  	} else if banner.Type(bannerToUpdate.BannerType) == banner.Org {
   134  		errs = r.BannerService.UpdateOrgBanner(ctx, bannerToUpdate)
   135  	}
   136  	if errs != nil {
   137  		return nil, errs
   138  	}
   139  	err = r.BannerService.UpdateBannerSQL(ctx, bannerToUpdate)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	if edgeSecurityCompliance != nil {
   144  		if err := r.BannerService.UpdateBannerEdgeSecurityCompliance(ctx, *edgeSecurityCompliance, bannerEdgeID); err != nil {
   145  			return nil, err
   146  		}
   147  	}
   148  	return mapper.ToEdgeResponsePayload(200, "Successfully updated "+bannerToUpdate.Name), nil
   149  }
   150  
   151  // DeleteBanner is the resolver for the deleteBanner field.
   152  func (r *mutationResolver) DeleteBanner(ctx context.Context, bannerEdgeID string) (*model.EdgeResponsePayload, error) {
   153  	var errList []string
   154  	var notFoundErrs []string
   155  	bannerToDelete, err := r.BannerService.GetBanner(ctx, bannerEdgeID)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	path, err := r.BannerService.GetBannerInfraBucketPath(ctx)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	var bslErr error
   166  	if banner.Type(bannerToDelete.BannerType) == banner.EU {
   167  		_, bslErr = r.BannerService.DeleteEUBanner(ctx, bannerToDelete.Name, bannerToDelete.BannerBSLId)
   168  	} else if banner.Type(bannerToDelete.BannerType) == banner.Org {
   169  		_, bslErr = r.BannerService.DeleteOrgBanner(ctx, bannerToDelete.BannerBSLId)
   170  	} else {
   171  		errList = append(errList, fmt.Sprintf("undefined banner type: %s", bannerToDelete.BannerType))
   172  	}
   173  
   174  	if bslErr != nil {
   175  		if apierror.IsForbiddenError(err) {
   176  			notFoundErrs = append(notFoundErrs, fmt.Sprintf("error in deleting banner: %s", bslErr.Error()))
   177  		} else {
   178  			errList = append(errList, fmt.Sprintf("error in deleting banner: %s", bslErr.Error()))
   179  			errList = append(errList, fmt.Sprintf("there was an error deleting banner: %s", bannerToDelete.Name))
   180  		}
   181  	}
   182  
   183  	bannerObject, bannerErr := r.BannerService.CreateBannerCr(ctx, bannerToDelete.BannerEdgeID, bannerToDelete.ProjectID, bannerToDelete.Name, bannerToDelete.BannerBSLId, make([]string, 0))
   184  	if bannerErr != nil {
   185  		errList = append(errList, fmt.Sprintf("error creating the banner cr: %s", bannerErr))
   186  	}
   187  	deleteBannerMessage := chariotClientApi.
   188  		NewChariotMessage().
   189  		SetOperation(chariotClientApi.Delete).
   190  		SetOwner(services.ComponentOwner).
   191  		SetBanner(r.Config.Bff.TopLevelProjectID).
   192  		SetCluster(path).
   193  		AddObject(bannerObject)
   194  
   195  	if err := r.ChariotService.InvokeChariotPubsub(ctx, deleteBannerMessage, nil); err != nil {
   196  		log.Ctx(ctx).Err(err).Msg("chariot invocation failed")
   197  		errList = append(errList, fmt.Sprintf("error calling chariot v2 and deleting a banner cr: %s", err))
   198  	}
   199  
   200  	clusterInfra, err := r.BannerService.GetClusterInfraInfo(ctx, bannerEdgeID)
   201  	if err != nil { //nolint: nestif
   202  		log.Ctx(ctx).Err(err).Msg("fail to get cluster infra cluster info")
   203  		errList = append(errList, fmt.Sprintf("error getting cluster infra cluster info: %s", err))
   204  	} else {
   205  		//delete cluster infra cr
   206  		storeClusterBase64, err := r.RegistrationService.CreateAClusterCR(ctx, cluster.GKE, clusterInfra.Name, clusterInfra.ProjectID, fleet.Cluster, "", clusterInfra.ClusterEdgeID, bannerToDelete, nil)
   207  		if err != nil {
   208  			log.Ctx(ctx).Err(err).Msg("fail to create infra cluster cr to delete")
   209  			errList = append(errList, fmt.Sprintf("fail to create infra cluster cr to delete: %s", err))
   210  		}
   211  		bannerInfra, errs := r.BannerService.GetBannerInfraInfo(ctx)
   212  		if errs != nil {
   213  			log.Ctx(ctx).Err(errs).Msg("fail to get banner infra cluster from db")
   214  			errList = append(errList, fmt.Sprintf("fail to get banner infra cluster from db: %s", errs))
   215  		}
   216  		chariotMessage := chariotClientApi.
   217  			NewChariotMessage().
   218  			SetBanner(bannerInfra.ProjectID).
   219  			SetCluster(path).
   220  			SetOperation(chariotClientApi.Delete).
   221  			SetOwner(services.ComponentOwner).
   222  			AddObject(storeClusterBase64)
   223  		if err := r.ChariotService.InvokeChariotPubsub(ctx, chariotMessage, nil); err != nil {
   224  			errList = append(errList, fmt.Sprintf("error calling chariot v2: %s", err))
   225  		}
   226  
   227  		errs = r.BannerService.DeleteClusterSQLEntry(ctx, clusterInfra.ClusterEdgeID)
   228  		if errs != nil {
   229  			errList = append(errList, fmt.Sprintf("error deleting cluster entry in sql: %s", errs))
   230  		}
   231  	}
   232  
   233  	if len(errList) == 0 {
   234  		bannerErr := r.BannerService.DeleteBannerSQLEntry(ctx, bannerToDelete.BannerEdgeID)
   235  		if bannerErr != nil {
   236  			errList = append(errList, fmt.Sprintf("error deleting banner entry in sql: %s", bannerErr))
   237  		}
   238  	}
   239  
   240  	if len(errList) > 0 {
   241  		errList = append(errList, notFoundErrs...)
   242  		return nil, fmt.Errorf(strings.Join(errList, "; "))
   243  	}
   244  	return mapper.ToEdgeResponsePayload(200, "Successfully deleted "+bannerToDelete.Name), nil
   245  }
   246  
   247  // AssignUserToBanner is the resolver for the assignUserToBanner field.
   248  func (r *mutationResolver) AssignUserToBanner(ctx context.Context, username string, bannerEdgeIds []string) (*model.EdgeResponsePayload, error) {
   249  	var euBannerIDs []string
   250  	var orgBannerNames []string
   251  	var orgBanners []*model.Banner
   252  	for _, b := range bannerEdgeIds {
   253  		bannerEdge, err := r.BannerService.GetBanner(ctx, b)
   254  		if err != nil {
   255  			return nil, err
   256  		}
   257  		if banner.Type(bannerEdge.BannerType) == banner.Org {
   258  			orgBannerNames = append(orgBannerNames, bannerEdge.Name)
   259  			orgBanners = append(orgBanners, bannerEdge)
   260  		} else {
   261  			euBannerIDs = append(euBannerIDs, bannerEdge.BannerBSLId)
   262  		}
   263  	}
   264  
   265  	err := r.BannerService.AssignUserToEUBanners(ctx, username, euBannerIDs)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	err = r.BannerService.AssignUserToOrgBanners(ctx, username, orgBanners)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  
   275  	message := fmt.Sprintf("Successfully assigned user %s to banners %s", username, append(euBannerIDs, orgBannerNames...))
   276  	return mapper.ToEdgeResponsePayload(200, message), nil
   277  }
   278  
   279  // AssignRoleToUser is the resolver for the assignRoleToUser field.
   280  func (r *mutationResolver) AssignRoleToUser(ctx context.Context, username string, role string) (*model.EdgeResponsePayload, error) {
   281  	if !model.Role(role).IsValid() {
   282  		return nil, errors.New("invalid edge role provided")
   283  	}
   284  	roles, err := r.RoleService.GetEdgeGroupsForUserUser(ctx, username)
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  	if len(roles) > 0 {
   289  		for _, roleName := range roles {
   290  			if err := r.RoleService.RevokeRoleFromUser(ctx, username, roleName); err != nil {
   291  				return nil, err
   292  			}
   293  		}
   294  	}
   295  	if err := r.RoleService.AddRoleToUser(ctx, username, role); err != nil {
   296  		return nil, err
   297  	}
   298  	return mapper.ToEdgeResponsePayload(200, fmt.Sprintf("%s role added successfully", role)), nil
   299  }
   300  
   301  // AssignRolesToUser is the resolver for the assignRolesToUser field.
   302  func (r *mutationResolver) AssignRolesToUser(ctx context.Context, username string, roles []string) (*model.EdgeResponsePayload, error) {
   303  	currentRoles, err := r.RoleService.GetEdgeGroupsForUserUser(ctx, username)
   304  	if err != nil {
   305  		return nil, fmt.Errorf("unable to fetch roles from this user: %s", username)
   306  	}
   307  
   308  	for _, role := range roles {
   309  		if !model.Role(role).IsValid() {
   310  			return nil, fmt.Errorf("invalid edge role provided: %s", role)
   311  		}
   312  	}
   313  
   314  	for _, roleName := range currentRoles {
   315  		if err := r.RoleService.RevokeRoleFromUser(ctx, username, roleName); err != nil {
   316  			return nil, err
   317  		}
   318  	}
   319  
   320  	// If user doesn't select `roles` --> revoke
   321  	if len(roles) == 0 {
   322  		return mapper.ToEdgeResponsePayload(200, "roles revoked successfully"), nil
   323  	}
   324  
   325  	for _, assignRole := range roles {
   326  		if err := r.RoleService.AddRoleToUser(ctx, username, assignRole); err != nil {
   327  			return nil, err
   328  		}
   329  	}
   330  	return mapper.ToEdgeResponsePayload(200, fmt.Sprintf("%s role added successfully", roles)), nil
   331  }
   332  
   333  // ReplaceAdditionalPermissions is the resolver for the replaceAdditionalPermissions field. The users are granted or revoked additional roles if they match against a fixed list within the API
   334  func (r *mutationResolver) ReplaceAdditionalPermissions(ctx context.Context, username string, roles []string) (*model.EdgeResponsePayload, error) {
   335  	// Filtered set of additional roles that will be used when assigning or revoking
   336  	additionalRoles := []model.Role{
   337  		model.RoleEdgeOiAdmin,
   338  		model.RoleEdgeSuperUser,
   339  		model.RoleEdgeL4,
   340  		model.RoleEdgeL3,
   341  		model.RoleEdgeL2,
   342  		model.RoleEdgeL1,
   343  	}
   344  
   345  	// Check incoming roles are valid
   346  	for _, role := range roles {
   347  		if !slices.Contains(additionalRoles, model.Role(role)) {
   348  			return nil, fmt.Errorf("edge role provided is not an additional privilege that can be assigned: %s", role)
   349  		}
   350  	}
   351  
   352  	// Get user's current roles
   353  	currentRoles, err := r.RoleService.GetEdgeGroupsForUserUser(ctx, username)
   354  	if err != nil {
   355  		return nil, fmt.Errorf("unable to fetch roles from this user: %s", username)
   356  	}
   357  
   358  	// Revoke any additional roles before we readd them in the next step
   359  	for _, roleName := range currentRoles {
   360  		if slices.Contains(additionalRoles, model.Role(roleName)) {
   361  			if err := r.RoleService.RevokeRoleFromUser(ctx, username, roleName); err != nil {
   362  				return nil, err
   363  			}
   364  		}
   365  	}
   366  
   367  	// If user passes in an empty set of roles --> revoke
   368  	if len(roles) == 0 {
   369  		return mapper.ToEdgeResponsePayload(200, "roles revoked successfully"), nil
   370  	}
   371  
   372  	// Assign additional roles
   373  	for _, assignRole := range roles {
   374  		if slices.Contains(additionalRoles, model.Role(assignRole)) {
   375  			if err := r.RoleService.AddRoleToUser(ctx, username, assignRole); err != nil {
   376  				return nil, err
   377  			}
   378  		}
   379  	}
   380  
   381  	return mapper.ToEdgeResponsePayload(200, fmt.Sprintf("%s role added successfully", roles)), nil
   382  }
   383  
   384  // Banners is the resolver for the banners field.
   385  func (r *queryResolver) Banners(ctx context.Context) ([]*model.Banner, error) {
   386  	banners, err := r.BannerService.GetBanners(ctx)
   387  	if err != nil {
   388  		return nil, err
   389  	}
   390  	return banners, nil
   391  }
   392  
   393  // Banner is the resolver for the banner field.
   394  func (r *queryResolver) Banner(ctx context.Context, bannerEdgeID string) (*model.Banner, error) {
   395  	banner, err := r.BannerService.GetBanner(ctx, bannerEdgeID)
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  	banner, err = r.BannerService.CheckBannerInBSL(ctx, banner)
   400  	if err != nil {
   401  		return banner, apierror.AddAPIErrorToResponse(ctx, err)
   402  	}
   403  	return banner, nil
   404  }
   405  
   406  // BannerStatus is the resolver for the bannerStatus field.
   407  func (r *queryResolver) BannerStatus(ctx context.Context, bannerEdgeID string) (*model.BannerStatus, error) {
   408  	status, err := r.BannerService.GetBannerInfraStatus(ctx, bannerEdgeID)
   409  	if err != nil {
   410  		return nil, err
   411  	}
   412  	return status, nil
   413  }
   414  

View as plain text