1 package resolver
2
3
4
5
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
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
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
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
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 {
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
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
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
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
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
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
334 func (r *mutationResolver) ReplaceAdditionalPermissions(ctx context.Context, username string, roles []string) (*model.EdgeResponsePayload, error) {
335
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
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
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
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
368 if len(roles) == 0 {
369 return mapper.ToEdgeResponsePayload(200, "roles revoked successfully"), nil
370 }
371
372
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
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
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
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