package resolver // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. // Code generated by github.com/99designs/gqlgen version v0.17.45 import ( "context" "errors" "fmt" "slices" "strings" "edge-infra.dev/pkg/edge/api/apierror" "edge-infra.dev/pkg/edge/api/graph/mapper" "edge-infra.dev/pkg/edge/api/graph/model" "edge-infra.dev/pkg/edge/api/services" "edge-infra.dev/pkg/edge/api/utils" chariotClientApi "edge-infra.dev/pkg/edge/chariot/client" "edge-infra.dev/pkg/edge/constants/api/banner" "edge-infra.dev/pkg/edge/constants/api/cluster" "edge-infra.dev/pkg/edge/constants/api/fleet" "github.com/google/uuid" "github.com/rs/zerolog/log" ) // CreateBanner is the resolver for the createBanner field. func (r *mutationResolver) CreateBanner(ctx context.Context, name string, description *string, isOrgBanner *bool, bslID *string) (*model.EdgeResponsePayload, error) { var ( bannerUUID = uuid.NewString() projectID = utils.GenerateProjectID() b *model.Banner err error bannerType = utils.GetBannerType(isOrgBanner) //organization = bsl.GetOrgShortName(middleware.ForContext(ctx).Organization) ) path, err := r.BannerService.GetBannerInfraBucketPath(ctx) if err != nil { return nil, err } switch bannerType { case banner.Org: if utils.IsNullOrEmpty(bslID) { b, err = r.BannerService.CreateOrganizationBanner(ctx, name, description) } else { existingBanner, errs := r.BannerService.GetBSLOrganization(ctx, *bslID) if errs != nil { return nil, errs } b = mapper.BSLOrgToBanner(existingBanner) if strings.Contains(b.Name, "deleted") { return nil, fmt.Errorf("bsl subOrg %s is deleted", *bslID) } if utils.IsNullOrEmpty(description) { description = &existingBanner.Description } } case banner.EU: if utils.IsNullOrEmpty(bslID) { b, err = r.BannerService.CreateEUBanner(ctx, name, description) } else { existingBanner, errs := r.BannerService.GetBSLEU(ctx, *bslID) if errs != nil { return nil, errs } b = mapper.EnterpriseUnitToBanner(existingBanner) if strings.Contains(b.Name, "deleted") || !existingBanner.Active { return nil, fmt.Errorf("enterprise unit %s is deleted", *bslID) } if utils.IsNullOrEmpty(description) { description = &existingBanner.Description } } } if err != nil { return nil, err } errs := r.BannerService.CreateBannerSQLEntry(ctx, projectID, name, string(bannerType), b.BannerBSLId, bannerUUID, *description) if errs != nil { err := fmt.Errorf("error creating banner entry in sql: %w", errs) undoErr := r.BannerService.UndoCreateBanner(ctx, name, b.BannerBSLId, bannerUUID, "", isOrgBanner, path) if undoErr != nil { err = fmt.Errorf("%w; (rollback errors: %s)", err, undoErr) } return nil, err } bannerObject, bannerCRErr := r.BannerService.CreateBannerCr(ctx, bannerUUID, projectID, name, b.BannerBSLId, make([]string, 0)) if bannerCRErr != nil { undoErr := r.BannerService.UndoCreateBanner(ctx, name, b.BannerBSLId, bannerUUID, "", isOrgBanner, path) if undoErr != nil { bannerCRErr = fmt.Errorf("%w; (rollback errors: %s)", bannerCRErr, undoErr) } return nil, bannerCRErr } createStoreClusterMessage := chariotClientApi. NewChariotMessage(). SetOperation(chariotClientApi.Create). SetOwner(services.ComponentOwner). SetBanner(r.Config.Bff.TopLevelProjectID). SetCluster(path). AddObject(bannerObject) if err := r.ChariotService.InvokeChariotPubsub(ctx, createStoreClusterMessage, nil); err != nil { err := fmt.Errorf("error calling chariot v2 and creating a banner cr: %w", err) log.Ctx(ctx).Err(err).Msg("chariot invocation failed") undoErr := r.BannerService.UndoCreateBanner(ctx, name, b.BannerBSLId, bannerUUID, "", isOrgBanner, path) if undoErr != nil { err = fmt.Errorf("%w; (rollback errors: %s)", err, undoErr) } return nil, err } return mapper.ToEdgeResponsePayload(200, "Successfully created "+b.Name), nil } // UpdateBanner is the resolver for the updateBanner field. func (r *mutationResolver) UpdateBanner(ctx context.Context, bannerEdgeID string, displayName *string, description *string, edgeSecurityCompliance *model.EdgeSecurityComplianceOptions) (*model.EdgeResponsePayload, error) { bannerToUpdate, err := r.BannerService.GetBanner(ctx, bannerEdgeID) if err != nil { return nil, err } if displayName != nil { bannerToUpdate.Name = *displayName } if description != nil { bannerToUpdate.Description = description } var errs error if banner.Type(bannerToUpdate.BannerType) == banner.EU { errs = r.BannerService.UpdateEUBanner(ctx, bannerToUpdate) } else if banner.Type(bannerToUpdate.BannerType) == banner.Org { errs = r.BannerService.UpdateOrgBanner(ctx, bannerToUpdate) } if errs != nil { return nil, errs } err = r.BannerService.UpdateBannerSQL(ctx, bannerToUpdate) if err != nil { return nil, err } if edgeSecurityCompliance != nil { if err := r.BannerService.UpdateBannerEdgeSecurityCompliance(ctx, *edgeSecurityCompliance, bannerEdgeID); err != nil { return nil, err } } return mapper.ToEdgeResponsePayload(200, "Successfully updated "+bannerToUpdate.Name), nil } // DeleteBanner is the resolver for the deleteBanner field. func (r *mutationResolver) DeleteBanner(ctx context.Context, bannerEdgeID string) (*model.EdgeResponsePayload, error) { var errList []string var notFoundErrs []string bannerToDelete, err := r.BannerService.GetBanner(ctx, bannerEdgeID) if err != nil { return nil, err } path, err := r.BannerService.GetBannerInfraBucketPath(ctx) if err != nil { return nil, err } var bslErr error if banner.Type(bannerToDelete.BannerType) == banner.EU { _, bslErr = r.BannerService.DeleteEUBanner(ctx, bannerToDelete.Name, bannerToDelete.BannerBSLId) } else if banner.Type(bannerToDelete.BannerType) == banner.Org { _, bslErr = r.BannerService.DeleteOrgBanner(ctx, bannerToDelete.BannerBSLId) } else { errList = append(errList, fmt.Sprintf("undefined banner type: %s", bannerToDelete.BannerType)) } if bslErr != nil { if apierror.IsForbiddenError(err) { notFoundErrs = append(notFoundErrs, fmt.Sprintf("error in deleting banner: %s", bslErr.Error())) } else { errList = append(errList, fmt.Sprintf("error in deleting banner: %s", bslErr.Error())) errList = append(errList, fmt.Sprintf("there was an error deleting banner: %s", bannerToDelete.Name)) } } bannerObject, bannerErr := r.BannerService.CreateBannerCr(ctx, bannerToDelete.BannerEdgeID, bannerToDelete.ProjectID, bannerToDelete.Name, bannerToDelete.BannerBSLId, make([]string, 0)) if bannerErr != nil { errList = append(errList, fmt.Sprintf("error creating the banner cr: %s", bannerErr)) } deleteBannerMessage := chariotClientApi. NewChariotMessage(). SetOperation(chariotClientApi.Delete). SetOwner(services.ComponentOwner). SetBanner(r.Config.Bff.TopLevelProjectID). SetCluster(path). AddObject(bannerObject) if err := r.ChariotService.InvokeChariotPubsub(ctx, deleteBannerMessage, nil); err != nil { log.Ctx(ctx).Err(err).Msg("chariot invocation failed") errList = append(errList, fmt.Sprintf("error calling chariot v2 and deleting a banner cr: %s", err)) } clusterInfra, err := r.BannerService.GetClusterInfraInfo(ctx, bannerEdgeID) if err != nil { //nolint: nestif log.Ctx(ctx).Err(err).Msg("fail to get cluster infra cluster info") errList = append(errList, fmt.Sprintf("error getting cluster infra cluster info: %s", err)) } else { //delete cluster infra cr storeClusterBase64, err := r.RegistrationService.CreateAClusterCR(ctx, cluster.GKE, clusterInfra.Name, clusterInfra.ProjectID, fleet.Cluster, "", clusterInfra.ClusterEdgeID, bannerToDelete, nil) if err != nil { log.Ctx(ctx).Err(err).Msg("fail to create infra cluster cr to delete") errList = append(errList, fmt.Sprintf("fail to create infra cluster cr to delete: %s", err)) } bannerInfra, errs := r.BannerService.GetBannerInfraInfo(ctx) if errs != nil { log.Ctx(ctx).Err(errs).Msg("fail to get banner infra cluster from db") errList = append(errList, fmt.Sprintf("fail to get banner infra cluster from db: %s", errs)) } chariotMessage := chariotClientApi. NewChariotMessage(). SetBanner(bannerInfra.ProjectID). SetCluster(path). SetOperation(chariotClientApi.Delete). SetOwner(services.ComponentOwner). AddObject(storeClusterBase64) if err := r.ChariotService.InvokeChariotPubsub(ctx, chariotMessage, nil); err != nil { errList = append(errList, fmt.Sprintf("error calling chariot v2: %s", err)) } errs = r.BannerService.DeleteClusterSQLEntry(ctx, clusterInfra.ClusterEdgeID) if errs != nil { errList = append(errList, fmt.Sprintf("error deleting cluster entry in sql: %s", errs)) } } if len(errList) == 0 { bannerErr := r.BannerService.DeleteBannerSQLEntry(ctx, bannerToDelete.BannerEdgeID) if bannerErr != nil { errList = append(errList, fmt.Sprintf("error deleting banner entry in sql: %s", bannerErr)) } } if len(errList) > 0 { errList = append(errList, notFoundErrs...) return nil, fmt.Errorf(strings.Join(errList, "; ")) } return mapper.ToEdgeResponsePayload(200, "Successfully deleted "+bannerToDelete.Name), nil } // AssignUserToBanner is the resolver for the assignUserToBanner field. func (r *mutationResolver) AssignUserToBanner(ctx context.Context, username string, bannerEdgeIds []string) (*model.EdgeResponsePayload, error) { var euBannerIDs []string var orgBannerNames []string var orgBanners []*model.Banner for _, b := range bannerEdgeIds { bannerEdge, err := r.BannerService.GetBanner(ctx, b) if err != nil { return nil, err } if banner.Type(bannerEdge.BannerType) == banner.Org { orgBannerNames = append(orgBannerNames, bannerEdge.Name) orgBanners = append(orgBanners, bannerEdge) } else { euBannerIDs = append(euBannerIDs, bannerEdge.BannerBSLId) } } err := r.BannerService.AssignUserToEUBanners(ctx, username, euBannerIDs) if err != nil { return nil, err } err = r.BannerService.AssignUserToOrgBanners(ctx, username, orgBanners) if err != nil { return nil, err } message := fmt.Sprintf("Successfully assigned user %s to banners %s", username, append(euBannerIDs, orgBannerNames...)) return mapper.ToEdgeResponsePayload(200, message), nil } // AssignRoleToUser is the resolver for the assignRoleToUser field. func (r *mutationResolver) AssignRoleToUser(ctx context.Context, username string, role string) (*model.EdgeResponsePayload, error) { if !model.Role(role).IsValid() { return nil, errors.New("invalid edge role provided") } roles, err := r.RoleService.GetEdgeGroupsForUserUser(ctx, username) if err != nil { return nil, err } if len(roles) > 0 { for _, roleName := range roles { if err := r.RoleService.RevokeRoleFromUser(ctx, username, roleName); err != nil { return nil, err } } } if err := r.RoleService.AddRoleToUser(ctx, username, role); err != nil { return nil, err } return mapper.ToEdgeResponsePayload(200, fmt.Sprintf("%s role added successfully", role)), nil } // AssignRolesToUser is the resolver for the assignRolesToUser field. func (r *mutationResolver) AssignRolesToUser(ctx context.Context, username string, roles []string) (*model.EdgeResponsePayload, error) { currentRoles, err := r.RoleService.GetEdgeGroupsForUserUser(ctx, username) if err != nil { return nil, fmt.Errorf("unable to fetch roles from this user: %s", username) } for _, role := range roles { if !model.Role(role).IsValid() { return nil, fmt.Errorf("invalid edge role provided: %s", role) } } for _, roleName := range currentRoles { if err := r.RoleService.RevokeRoleFromUser(ctx, username, roleName); err != nil { return nil, err } } // If user doesn't select `roles` --> revoke if len(roles) == 0 { return mapper.ToEdgeResponsePayload(200, "roles revoked successfully"), nil } for _, assignRole := range roles { if err := r.RoleService.AddRoleToUser(ctx, username, assignRole); err != nil { return nil, err } } return mapper.ToEdgeResponsePayload(200, fmt.Sprintf("%s role added successfully", roles)), nil } // 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 func (r *mutationResolver) ReplaceAdditionalPermissions(ctx context.Context, username string, roles []string) (*model.EdgeResponsePayload, error) { // Filtered set of additional roles that will be used when assigning or revoking additionalRoles := []model.Role{ model.RoleEdgeOiAdmin, model.RoleEdgeSuperUser, model.RoleEdgeL4, model.RoleEdgeL3, model.RoleEdgeL2, model.RoleEdgeL1, } // Check incoming roles are valid for _, role := range roles { if !slices.Contains(additionalRoles, model.Role(role)) { return nil, fmt.Errorf("edge role provided is not an additional privilege that can be assigned: %s", role) } } // Get user's current roles currentRoles, err := r.RoleService.GetEdgeGroupsForUserUser(ctx, username) if err != nil { return nil, fmt.Errorf("unable to fetch roles from this user: %s", username) } // Revoke any additional roles before we readd them in the next step for _, roleName := range currentRoles { if slices.Contains(additionalRoles, model.Role(roleName)) { if err := r.RoleService.RevokeRoleFromUser(ctx, username, roleName); err != nil { return nil, err } } } // If user passes in an empty set of roles --> revoke if len(roles) == 0 { return mapper.ToEdgeResponsePayload(200, "roles revoked successfully"), nil } // Assign additional roles for _, assignRole := range roles { if slices.Contains(additionalRoles, model.Role(assignRole)) { if err := r.RoleService.AddRoleToUser(ctx, username, assignRole); err != nil { return nil, err } } } return mapper.ToEdgeResponsePayload(200, fmt.Sprintf("%s role added successfully", roles)), nil } // Banners is the resolver for the banners field. func (r *queryResolver) Banners(ctx context.Context) ([]*model.Banner, error) { banners, err := r.BannerService.GetBanners(ctx) if err != nil { return nil, err } return banners, nil } // Banner is the resolver for the banner field. func (r *queryResolver) Banner(ctx context.Context, bannerEdgeID string) (*model.Banner, error) { banner, err := r.BannerService.GetBanner(ctx, bannerEdgeID) if err != nil { return nil, err } banner, err = r.BannerService.CheckBannerInBSL(ctx, banner) if err != nil { return banner, apierror.AddAPIErrorToResponse(ctx, err) } return banner, nil } // BannerStatus is the resolver for the bannerStatus field. func (r *queryResolver) BannerStatus(ctx context.Context, bannerEdgeID string) (*model.BannerStatus, error) { status, err := r.BannerService.GetBannerInfraStatus(ctx, bannerEdgeID) if err != nil { return nil, err } return status, nil }