1 package services
2
3 import (
4 "context"
5 "database/sql"
6 "encoding/json"
7 "fmt"
8 "net/http"
9 "strings"
10 "time"
11
12 "github.com/lib/pq"
13 "github.com/rs/zerolog/log"
14 "k8s.io/utils/ptr"
15
16 "edge-infra.dev/pkg/edge/api/apierror"
17 sqlerr "edge-infra.dev/pkg/edge/api/apierror/sql"
18 "edge-infra.dev/pkg/edge/api/bsl/types"
19 "edge-infra.dev/pkg/edge/api/graph/mapper"
20 "edge-infra.dev/pkg/edge/api/graph/model"
21 "edge-infra.dev/pkg/edge/api/middleware"
22 sqlquery "edge-infra.dev/pkg/edge/api/sql"
23 apiTypes "edge-infra.dev/pkg/edge/api/types"
24 "edge-infra.dev/pkg/edge/api/utils"
25 bannerv1alpha1 "edge-infra.dev/pkg/edge/apis/banner/v1alpha1"
26 "edge-infra.dev/pkg/edge/bsl"
27 chariotClientApi "edge-infra.dev/pkg/edge/chariot/client"
28 "edge-infra.dev/pkg/edge/constants/api/banner"
29 "edge-infra.dev/pkg/edge/controllers/util/edgedb"
30 "edge-infra.dev/pkg/sds/clustersecrets/audit"
31 )
32
33 const (
34 BspEnterpriseUnitPath = "/provisioning/enterprise-units"
35 getEnterpriseUnit = "/provisioning/enterprise-units/%s"
36 updateOrganization = "/provisioning/organizations/%s"
37 bspOrgPath = "/provisioning/organizations"
38 bspExternalUserPath = "/provisioning/users/external"
39 bspEnterpriseUnitOrganizationsPath = BspEnterpriseUnitPath + "?typeNamePattern=organization"
40 bspDeleteOrgPath = "/provisioning/organizations/%s/purge"
41 bspSubOrganizationsPath = "/provisioning/organizations/find-by-parent?parentOrganizationName=%s" + bspPaging
42 bspUserAssignedSubOrgsPath = "/provisioning/users/external/%s/accounts?pageNumber=%d&pageSize=%d"
43 bspPaging = "&pageNumber=%d&pageSize=%d"
44 bspEnterpriseUnitGrantBasePath = "/provisioning/enterprise-unit-grants/unit-grants/%s/%s"
45 bspGetAllAssignedOrgURL = "/provisioning/enterprise-unit-grants/unit-grants?username=%s"
46 bspGetOrgUsersListPath = "/provisioning/enterprise-unit-grants/unit-grants?enterpriseUnitId=%s&pageNumber=%d&pageSize=%d"
47 bspUserDetailsPath = "/provisioning/users/user-details"
48 pageSize = 200
49 commerceOrg = "commerce"
50 ProvisionedStatus = "PROVISIONED"
51 ProvisioningStatusMessage = "Edge Provisioning"
52 )
53
54
55 type BannerService interface {
56 GetBannerTenantInfo(ctx context.Context, orgName string) (*model.Tenant, error)
57 GetBanners(ctx context.Context) ([]*model.Banner, error)
58 GetBannerCRByID(ctx context.Context, id string) (*bannerv1alpha1.Banner, error)
59 GetBanner(ctx context.Context, bannerEdgeID string) (*model.Banner, error)
60 CheckBannerInBSL(ctx context.Context, bannerModel *model.Banner) (*model.Banner, error)
61 CreateEUBanner(ctx context.Context, name string, description *string) (*model.Banner, error)
62 CreateOrganizationBanner(ctx context.Context, name string, description *string) (*model.Banner, error)
63 UndoCreateBanner(ctx context.Context, name, id, uuid, bannerObject string, isOrgBanner *bool, bucketPath string) error
64 DeleteEUBanner(ctx context.Context, name, id string) (bool, error)
65 DeleteOrgBanner(ctx context.Context, id string) (bool, error)
66 AssignUserToEUBanners(ctx context.Context, username string, banners []string) error
67 AssignUserToOrgBanners(ctx context.Context, username string, banners []*model.Banner) error
68 GetUsersForEuBanner(ctx context.Context, organizationID string) ([]*model.User, error)
69 GetBSLOrganization(ctx context.Context, organization string) (*bsl.BSLOrganization, error)
70 GetUserAssignedBanners(ctx context.Context, username string) ([]*model.BannerInfo, error)
71 CreateBannerCr(ctx context.Context, uuid, projectID, name, enterpriseUnitID string, enablements []string) (string, error)
72 DeleteBannerSQLEntry(ctx context.Context, bannerUUID string) error
73 CreateBannerSQLEntry(ctx context.Context, projectID, bannerName, bannerType, enterpriseUnitID, bannerUUID, description string) error
74 GetBannerProjectID(ctx context.Context, bannerEdgeID string) (string, error)
75 GetBannerTypeFromID(ctx context.Context, id string) (banner.Type, error)
76 GetBannerTypeFromUUID(ctx context.Context, uuid string) (banner.Type, error)
77 GetBannerByEdgeID(ctx context.Context, bannerUUID string) (*model.Banner, error)
78 GetBannerByBSLID(ctx context.Context, bannerBSLID string) (*model.Banner, error)
79 GetEdgeBannerInfo(ctx context.Context, banners []*model.Banner) []*model.Banner
80 GetClusterInfraInfo(ctx context.Context, bannerEdgeID string) (*model.Cluster, error)
81 GetBannerByNameAndTenant(ctx context.Context, name, orgName string) (*model.Banner, error)
82 DeleteClusterSQLEntry(ctx context.Context, clusterEdgeID string) error
83 GetBannerInfraInfo(ctx context.Context) (*model.Cluster, error)
84 GetBannerInfraBucketPath(ctx context.Context) (string, error)
85 UpdateBannerSQL(ctx context.Context, update *model.Banner) error
86 UpdateEUBanner(ctx context.Context, update *model.Banner) error
87 UpdateOrgBanner(ctx context.Context, update *model.Banner) error
88 GetBSLEU(ctx context.Context, eu string) (*types.EnterpriseUnitData, error)
89 UpdateBannerRemoteAccessIP(ctx context.Context, remoteAccessIP string, bannerEdgeID string) error
90 GetBannerRemoteAccessIP(ctx context.Context, bannerEdgeID string) (string, error)
91 GetBannerInfraStatus(ctx context.Context, bannerEdgeID string) (*model.BannerStatus, error)
92 UpdateBannerEdgeSecurityCompliance(ctx context.Context, optInEdgeSecurityCompliance model.EdgeSecurityComplianceOptions, bannerEdgeID string) error
93 }
94
95 type bannerService struct {
96 GkeService GkeClient
97 ChariotService ChariotService
98 BSLClient *bsl.Client
99 TopLevelProjectID string
100 SQLDB *sql.DB
101 config *apiTypes.Config
102 }
103
104 func (o *bannerService) UpdateBannerSQL(ctx context.Context, update *model.Banner) error {
105 _, err := o.SQLDB.ExecContext(ctx, sqlquery.UpdateBanner, update.Name, update.Description, update.BannerEdgeID)
106 if err != nil {
107 return sqlerr.Wrap(err)
108 }
109 return nil
110 }
111
112 func (o *bannerService) UpdateEUBanner(ctx context.Context, update *model.Banner) error {
113 desc := ""
114 if update.Description != nil {
115 desc = *update.Description
116 }
117 payload := mapper.ToEnterpriseUnitData(update.BannerBSLId, update.Name, desc, types.IsOrganization, true)
118 err := o.BSLClient.WithUserTokenCredentials(ctx).SetPayload(payload).Put(BspEnterpriseUnitPath)
119 if err != nil {
120 log.Err(err).Msg("error from bsp while updating an eu")
121 }
122 return err
123 }
124
125 func (o *bannerService) UpdateOrgBanner(ctx context.Context, update *model.Banner) error {
126 desc := ""
127 if update.Description != nil {
128 desc = *update.Description
129 }
130 orgData := &types.UpdateOrganizationData{
131 Description: desc,
132 DisplayName: update.Name,
133 }
134 err := o.BSLClient.WithUserTokenCredentials(ctx).SetPayload(orgData).Put(fmt.Sprintf(updateOrganization, update.BannerBSLId))
135 if err != nil {
136 log.Err(err).Msg("error from bsp while updating an org")
137 }
138 return err
139 }
140
141 func (o *bannerService) GetBanner(ctx context.Context, bannerEdgeID string) (*model.Banner, error) {
142 var (
143 optedInEdgeSecurityCompliance *bool
144 )
145
146 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerByBannerID, bannerEdgeID)
147 bannerModel := &model.Banner{}
148 bannerModel.BslEntityTypes = []string{}
149 err := row.Scan(&bannerModel.BannerEdgeID, &bannerModel.BannerBSLId, &bannerModel.Name, &bannerModel.BannerType, &bannerModel.Description, &bannerModel.ProjectID, &bannerModel.TenantEdgeID, &bannerModel.BslDataSynced, pq.Array(&bannerModel.BslEntityTypes), &optedInEdgeSecurityCompliance)
150 if err != nil {
151 return bannerModel, sqlerr.Wrap(err)
152 }
153
154 bannerModel.OptInEdgeSecurityCompliance = o.config.EdgeOptInSecurityCompliance
155 if optedInEdgeSecurityCompliance != nil {
156 bannerModel.OptInEdgeSecurityCompliance = *optedInEdgeSecurityCompliance
157 }
158 return bannerModel, nil
159 }
160
161 func (o *bannerService) GetBannerInfraStatus(ctx context.Context, bannerEdgeID string) (*model.BannerStatus, error) {
162 status := &model.BannerStatus{}
163 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerInfraStatusByBannerEdgeID, bannerEdgeID)
164 if err := row.Scan(&status.Reason, &status.Message, &status.LastUpdatedAt); err != nil {
165 return nil, sqlerr.Wrap(err)
166 }
167 switch status.Reason {
168 case string(edgedb.InfraStatusReady):
169 status.Reason = ProvisionedStatus
170 status.Ready = true
171 default:
172 status.Reason = string(edgedb.InfraStatusProvisioning)
173
174
175
176
177 if status.Message == "" {
178 status.Message = ProvisioningStatusMessage
179 }
180 if status.LastUpdatedAt == "" {
181 status.LastUpdatedAt = time.Now().Format(time.RFC3339)
182 }
183 }
184 return status, nil
185 }
186
187 func (o *bannerService) CheckBannerInBSL(ctx context.Context, bannerModel *model.Banner) (*model.Banner, error) {
188 matchErr := apierror.New(apierror.BannerCheckMessage)
189 var bslBanner *model.Banner
190 if banner.Type(bannerModel.BannerType) == banner.Org {
191 org, err := o.GetBSLOrganization(ctx, bannerModel.BannerBSLId)
192 if err != nil {
193 return bannerModel, matchErr.AddGenericErrorExtension(apierror.BannerCheckKey, []string{fmt.Sprintf("banner %s not found in bsl: %s", bannerModel.Name, err.Error())})
194 }
195 bslBanner = mapper.BSLOrgToBanner(org)
196 } else {
197 eu, err := o.GetBSLEU(ctx, bannerModel.BannerBSLId)
198 if err != nil {
199 return bannerModel, matchErr.AddGenericErrorExtension(apierror.BannerCheckKey, []string{fmt.Sprintf("banner %s not found in bsl: %s", bannerModel.Name, err.Error())})
200 }
201 bslBanner = mapper.EnterpriseUnitToBanner(eu)
202 }
203 bannerModel, matchErr = bannerCheck(bslBanner, bannerModel, matchErr)
204 if len(matchErr.Ext[apierror.BannerCheckKey].([]string)) == 0 {
205 return bannerModel, nil
206 }
207 return bannerModel, matchErr
208 }
209
210 func (o *bannerService) GetBannerByNameAndTenant(ctx context.Context, name, orgName string) (*model.Banner, error) {
211 var (
212 optedInEdgeSecurityCompliance *bool
213 )
214
215 var tenant model.Tenant
216 row := o.SQLDB.QueryRowContext(ctx, sqlquery.TenantGetByName, orgName)
217 if err := row.Scan(&tenant.TenantEdgeID, &tenant.TenantBSLId, &tenant.OrgName); err != nil {
218 return nil, sqlerr.Wrap(err)
219 }
220 row = o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerByBannerByNameAndTenant, name, tenant.TenantEdgeID)
221 bannerModel := model.Banner{}
222 err := row.Scan(&bannerModel.BannerEdgeID, &bannerModel.BannerBSLId, &bannerModel.Name, &bannerModel.BannerType, &bannerModel.Description, &bannerModel.ProjectID, &bannerModel.TenantEdgeID, &optedInEdgeSecurityCompliance)
223 if err != nil {
224 return nil, sqlerr.Wrap(err)
225 }
226 bannerModel.OptInEdgeSecurityCompliance = o.config.EdgeOptInSecurityCompliance
227 if optedInEdgeSecurityCompliance != nil {
228 bannerModel.OptInEdgeSecurityCompliance = *optedInEdgeSecurityCompliance
229 }
230 return &bannerModel, nil
231 }
232
233 func (o *bannerService) GetBannerTenantInfo(ctx context.Context, orgName string) (*model.Tenant, error) {
234 var orgUUID, id, name string
235 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerTenantByOrgName, orgName)
236 err := row.Scan(&orgUUID, &id, &name)
237 tenant := &model.Tenant{
238 TenantEdgeID: orgUUID,
239 TenantBSLId: id,
240 OrgName: name,
241 }
242 if err != nil {
243 return tenant, sqlerr.Wrap(err)
244 }
245 return tenant, nil
246 }
247
248 func (o *bannerService) GetBannerProjectID(ctx context.Context, bannerEdgeID string) (string, error) {
249 var projectID string
250 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetProjectIDByBanner, bannerEdgeID)
251 err := row.Scan(&projectID)
252 if err != nil {
253 return projectID, sqlerr.Wrap(err)
254 }
255 return projectID, nil
256 }
257
258
259 func (o *bannerService) GetBanners(ctx context.Context) ([]*model.Banner, error) {
260 user := middleware.ForContext(ctx)
261 var (
262 banners []*model.Banner
263 eus []*types.EnterpriseUnitData
264 orgs []*types.OrganizationViewData
265 err error
266 )
267 if utils.Contains(user.Roles, string(model.RoleEdgeOrgAdmin)) {
268 eus, err = getAllEUs(o.BSLClient.WithUserTokenCredentials(ctx))
269 if err != nil {
270 return nil, err
271 }
272 banners = append(banners, mapper.EnterpriseUnitsToBanners(eus)...)
273 orgName := bsl.GetOrgShortName(user.Organization)
274 orgs, err = getAllSubOrgs(o.BSLClient.WithUserTokenCredentials(ctx), orgName)
275 if err != nil {
276 return nil, err
277 }
278 banners = append(banners, mapper.SubOrgsToBanners(orgs)...)
279 } else {
280 banners, err = o.getUserAssignedBanners(ctx, bsl.CreateFullAccountName(user))
281 if err != nil {
282 return nil, err
283 }
284 }
285 if len(banners) == 0 {
286 return banners, nil
287 }
288 return o.GetEdgeBannerInfo(ctx, banners), nil
289 }
290
291 func (o *bannerService) GetEdgeBannerInfo(ctx context.Context, banners []*model.Banner) []*model.Banner {
292 edgeBanners := []*model.Banner{}
293 for _, banner := range banners {
294 matchErr := apierror.New(apierror.BannerCheckMessage)
295 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerByBSLID, banner.BannerBSLId)
296 sqlInfo := &model.Banner{}
297 sqlInfo.BslEntityTypes = []string{}
298 var optedInEdgeSecurityCompliance *bool
299 err := row.Scan(&sqlInfo.BannerEdgeID, &sqlInfo.BannerBSLId, &sqlInfo.Name, &sqlInfo.BannerType, &sqlInfo.ProjectID, &sqlInfo.TenantEdgeID, &sqlInfo.Description, &sqlInfo.BslDataSynced, pq.Array(&sqlInfo.BslEntityTypes), &optedInEdgeSecurityCompliance)
300 if err != nil {
301 _, matchErr = bannerCheck(banner, nil, matchErr)
302 if len(matchErr.Ext[apierror.BannerCheckKey].([]string)) > 0 {
303 banner.MismatchInfo = matchErr.Ext[apierror.BannerCheckKey].([]string)
304 }
305 } else {
306 banner, matchErr = bannerCheck(banner, sqlInfo, matchErr)
307 if len(matchErr.Ext[apierror.BannerCheckKey].([]string)) > 0 {
308 banner.MismatchInfo = matchErr.Ext[apierror.BannerCheckKey].([]string)
309 }
310 banner.OptInEdgeSecurityCompliance = o.config.EdgeOptInSecurityCompliance
311 if optedInEdgeSecurityCompliance != nil {
312 banner.OptInEdgeSecurityCompliance = *optedInEdgeSecurityCompliance
313 }
314 edgeBanners = append(edgeBanners, banner)
315 }
316 }
317 return edgeBanners
318 }
319
320 func (o *bannerService) UpdateBannerRemoteAccessIP(ctx context.Context, remoteAccessIP string, bannerEdgeID string) error {
321 if _, err := o.SQLDB.ExecContext(ctx, sqlquery.UpdateBannerRemoteAccessIP, remoteAccessIP, bannerEdgeID); err != nil {
322 return sqlerr.Wrap(err)
323 }
324 return nil
325 }
326
327 func (o *bannerService) UpdateBannerEdgeSecurityCompliance(ctx context.Context, optInEdgeSecurityCompliance model.EdgeSecurityComplianceOptions, bannerEdgeID string) error {
328 user := middleware.ForContext(ctx)
329 auditLog := audit.New("api")
330 var optInSetting *bool
331 switch optInEdgeSecurityCompliance {
332 case model.EdgeSecurityComplianceOptionsOptOut:
333 optInSetting = ptr.To(false)
334 case model.EdgeSecurityComplianceOptionsOptIn:
335 optInSetting = ptr.To(true)
336 case model.EdgeSecurityComplianceOptionsDefault:
337 optInSetting = nil
338 }
339 if _, err := o.SQLDB.ExecContext(ctx, sqlquery.UpdateBannerEdgeSecurityCompliance, optInSetting, bannerEdgeID); err != nil {
340 return sqlerr.Wrap(err)
341 }
342 var complianceType string
343 if optInSetting == nil {
344 complianceType = audit.ComplianceDefault
345 } else if *optInSetting {
346 complianceType = audit.ComplianceOptIn
347 } else if !*optInSetting {
348 complianceType = audit.ComplianceOptOut
349 }
350 auditLog.LogComplianceChange(complianceType, "action_by", user.Username, "bannerEdgeID", bannerEdgeID)
351 return nil
352 }
353
354 func bannerCheck(bslBanner, sqlBanner *model.Banner, matchErr *apierror.Error) (*model.Banner, *apierror.Error) {
355 message := []string{}
356 if val, ok := matchErr.Ext[apierror.BannerCheckKey]; ok {
357 message = val.([]string)
358 }
359 if sqlBanner == nil {
360 message = append(message, fmt.Sprintf("matching row not found for bsl banner: %s", bslBanner.Name))
361 return bslBanner, matchErr.AddGenericErrorExtension(apierror.BannerCheckKey, message)
362 }
363
364
365 if bslBanner.Name != sqlBanner.Name {
366 message = append(message, fmt.Sprintf("name not match for bsl banner %s (bsl name: %s, sql name: %s)", bslBanner.Name, bslBanner.Name, sqlBanner.Name))
367 sqlBanner.Name = bslBanner.Name
368 }
369
370 bslDescription := ""
371 sqlDescription := ""
372 if bslBanner.Description != nil {
373 bslDescription = *bslBanner.Description
374 }
375 if sqlBanner.Description != nil {
376 sqlDescription = *sqlBanner.Description
377 }
378 if bslDescription != sqlDescription {
379 message = append(message, fmt.Sprintf("description not match for bsl banner %s (bsl description: %s, sql description: %s)", bslBanner.Name, bslDescription, sqlDescription))
380 sqlBanner.Description = bslBanner.Description
381 }
382 return sqlBanner, matchErr.AddGenericErrorExtension(apierror.BannerCheckKey, message)
383 }
384
385
386 func (o *bannerService) GetBannerNameByBannerID(ctx context.Context, id string) (string, error) {
387 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerNameByBannerID, id)
388 var name string
389 if err := row.Scan(&name); err != nil {
390 return "", sqlerr.Wrap(err)
391 }
392 return name, nil
393 }
394
395
396
397 func (o *bannerService) GetBannerEdgeIDByBannerID(ctx context.Context, id string) (string, error) {
398 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerEdgeIDByBannerID, id)
399 var name string
400 if err := row.Scan(&name); err != nil {
401 return "", sqlerr.Wrap(err)
402 }
403 return name, nil
404 }
405
406
407 func (o *bannerService) GetClusterInfraInfo(ctx context.Context, bannerEdgeID string) (*model.Cluster, error) {
408 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetClusterInfraInfo, bannerEdgeID)
409 var clusterEdgeID, clusterName, projectID string
410 var active bool
411 if err := row.Scan(&clusterEdgeID, &clusterName, &projectID, &active); err != nil {
412 return nil, sqlerr.Wrap(err)
413 }
414 cluster := &model.Cluster{
415 ClusterEdgeID: clusterEdgeID,
416 Name: clusterName,
417 ProjectID: projectID,
418 Active: &active,
419 BannerEdgeID: bannerEdgeID,
420 }
421 return cluster, nil
422 }
423
424 func (o *bannerService) GetBannerInfraInfo(ctx context.Context) (*model.Cluster, error) {
425 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerInfraInfo)
426 var clusterEdgeID, clusterName, projectID string
427 var active bool
428 if err := row.Scan(&clusterEdgeID, &clusterName, &projectID, &active); err != nil {
429 return nil, sqlerr.Wrap(err)
430 }
431 cluster := &model.Cluster{
432 ClusterEdgeID: clusterEdgeID,
433 Name: clusterName,
434 ProjectID: projectID,
435 Active: &active,
436 BannerEdgeID: "",
437 }
438 return cluster, nil
439 }
440
441 func (o *bannerService) GetBannerInfraBucketPath(ctx context.Context) (string, error) {
442 bi, err := o.GetBannerInfraInfo(ctx)
443 if err != nil {
444 return "", err
445 }
446 return bi.ClusterEdgeID, nil
447 }
448
449 func (o *bannerService) GetBannerByEdgeID(ctx context.Context, bannerUUID string) (*model.Banner, error) {
450 var (
451 optedInEdgeSecurityCompliance *bool
452 )
453 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerByID, bannerUUID)
454 var uuid, bannerID, bannerName, bannerType, projectID, tenant, description string
455 if err := row.Scan(&uuid, &bannerID, &bannerName, &bannerType, &projectID, &tenant, &description, &optedInEdgeSecurityCompliance); err != nil {
456 return nil, sqlerr.Wrap(err)
457 }
458 bannerModel := &model.Banner{
459 BannerBSLId: bannerID,
460 Name: bannerName,
461 Description: &description,
462 BannerType: bannerType,
463 TenantEdgeID: tenant,
464 ProjectID: projectID,
465 BannerEdgeID: bannerUUID,
466 }
467
468 bannerModel.OptInEdgeSecurityCompliance = o.config.EdgeOptInSecurityCompliance
469 if optedInEdgeSecurityCompliance != nil {
470 bannerModel.OptInEdgeSecurityCompliance = *optedInEdgeSecurityCompliance
471 }
472 return bannerModel, nil
473 }
474
475 func (o *bannerService) GetBannerByBSLID(ctx context.Context, bannerBSLID string) (*model.Banner, error) {
476 banner := &model.Banner{}
477 banner.BslEntityTypes = []string{}
478 var optedInEdgeSecurityCompliance *bool
479 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerByBSLID, bannerBSLID)
480 err := row.Scan(&banner.BannerEdgeID, &banner.BannerBSLId, &banner.Name, &banner.BannerType, &banner.ProjectID, &banner.TenantEdgeID, &banner.Description, &banner.BslDataSynced, pq.Array(&banner.BslEntityTypes), &optedInEdgeSecurityCompliance)
481 if err != nil {
482 log.Ctx(ctx).Err(err).Msg("matching row not found for bsl banner: " + bannerBSLID)
483 return nil, sqlerr.Wrap(err)
484 }
485 banner.OptInEdgeSecurityCompliance = o.config.EdgeOptInSecurityCompliance
486 if optedInEdgeSecurityCompliance != nil {
487 banner.OptInEdgeSecurityCompliance = *optedInEdgeSecurityCompliance
488 }
489 return banner, nil
490 }
491
492
493 func (o *bannerService) GetBannerCRByID(ctx context.Context, bannerUUID string) (*bannerv1alpha1.Banner, error) {
494 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerByID, bannerUUID)
495
496 var uuid, bannerID, bannerName, bannerType, projectID, tenant, description string
497 var edgeOptInSecurityCompliance *bool
498 if err := row.Scan(&uuid, &bannerID, &bannerName, &bannerType, &projectID, &tenant, &description, &edgeOptInSecurityCompliance); err != nil {
499 return nil, sqlerr.Wrap(err)
500 }
501
502
503 rows, err := o.SQLDB.QueryContext(ctx, sqlquery.ListCapabilitiesByBannerQuery, bannerUUID)
504 if err != nil {
505 return nil, sqlerr.Wrap(err)
506 }
507 var capabilities []string
508 defer rows.Close()
509 for rows.Next() {
510 var capability model.Capability
511 if err = rows.Scan(&capability.UUID, &capability.Name, &capability.Description); err != nil {
512 return nil, sqlerr.Wrap(err)
513 }
514 capabilities = append(capabilities, capability.Name)
515 }
516 if err := rows.Err(); err != nil {
517 return nil, sqlerr.Wrap(err)
518 }
519
520 user := middleware.ForContext(ctx)
521 bslName := bsl.GetOrgShortName(user.Organization)
522
523 return utils.NewBanner(uuid, bannerName, bslName, bannerID, projectID, capabilities), nil
524 }
525
526
527 func (o *bannerService) GetBSLOrganization(ctx context.Context, organization string) (*bsl.BSLOrganization, error) {
528 data := &bsl.BSLOrganization{}
529 err := o.BSLClient.WithUserTokenCredentials(ctx).JSON(http.MethodGet, fmt.Sprintf("%s/%s", BslOrgPath, organization), data)
530 if err != nil {
531 return nil, err
532 }
533 return data, nil
534 }
535
536 func (o *bannerService) GetBSLEU(ctx context.Context, eu string) (*types.EnterpriseUnitData, error) {
537 data := &types.EnterpriseUnitData{}
538 err := o.BSLClient.WithUserTokenCredentials(ctx).JSON(http.MethodGet, fmt.Sprintf(getEnterpriseUnit, eu), data)
539 if err != nil {
540 return nil, err
541 }
542 return data, nil
543 }
544
545 func (o *bannerService) GetBannerTypeFromID(ctx context.Context, id string) (banner.Type, error) {
546 var bannerType string
547 err := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerTypeFromIDQuery, id).Scan(&bannerType)
548 if err != nil {
549 return "", sqlerr.Wrap(err)
550 }
551 return banner.Type(bannerType), nil
552 }
553
554 func (o *bannerService) GetBannerTypeFromUUID(ctx context.Context, uuid string) (banner.Type, error) {
555 var bannerType string
556 err := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerTypeFromUUIDQuery, uuid).Scan(&bannerType)
557 if err != nil {
558 return "", sqlerr.Wrap(err)
559 }
560 return banner.Type(bannerType), nil
561 }
562
563 func (o *bannerService) GetBannerTypeFromNameAndTenant(ctx context.Context, name, tenant string) (banner.Type, error) {
564 var bannerType string
565 err := o.SQLDB.QueryRowContext(ctx, sqlquery.GetBannerTypeFromNameAndTenantQuery, name, tenant).Scan(&bannerType)
566 if err != nil {
567 return "", sqlerr.Wrap(err)
568 }
569 return banner.Type(bannerType), nil
570 }
571
572 func (o *bannerService) GetUserAssignedBanners(ctx context.Context, username string) ([]*model.BannerInfo, error) {
573 banners, err := o.getUserAssignedBanners(ctx, username)
574 if err != nil {
575 return make([]*model.BannerInfo, 0), err
576 }
577 for _, banner := range banners {
578 sqlBanner, err := o.GetBannerByBSLID(ctx, banner.BannerBSLId)
579 if err != nil {
580 return nil, err
581 }
582 banner.BannerEdgeID = sqlBanner.BannerEdgeID
583 banner.BannerType = sqlBanner.BannerType
584 }
585 return toBannersList(banners), nil
586 }
587
588 func (o *bannerService) GetBannerRemoteAccessIP(ctx context.Context, bannerEdgeID string) (string, error) {
589 var remoteAccessIP string
590 if err := o.SQLDB.QueryRowContext(ctx, sqlquery.GetRemoteAccessIPByBannerEdgeID, bannerEdgeID).Scan(&remoteAccessIP); err != nil {
591 return "", sqlerr.Wrap(err)
592 }
593 return remoteAccessIP, nil
594 }
595
596 func toBannersList(data []*model.Banner) []*model.BannerInfo {
597 banners := make([]*model.BannerInfo, 0)
598 for _, banner := range data {
599 banners = append(banners, &model.BannerInfo{BannerBSLId: banner.BannerBSLId, Name: banner.Name, BannerType: banner.BannerType, BannerEdgeID: banner.BannerEdgeID})
600 }
601 return banners
602 }
603
604 func (o *bannerService) getUserAssignedBanners(ctx context.Context, username string) ([]*model.Banner, error) {
605 banners := make([]*model.Banner, 0)
606 lowercaseUsername := strings.ToLower(username)
607 orgs, err := o.getUserAssignedOrgs(ctx, lowercaseUsername)
608 if err != nil {
609 return nil, err
610 }
611 banners = append(banners, mapper.SubExternalUsersToBanners(orgs)...)
612 eus, err := o.getUserAssignedEUs(ctx, lowercaseUsername)
613 if err != nil {
614 return nil, err
615 }
616 banners = append(banners, mapper.EnterpriseUnitsToBanners(eus)...)
617 return banners, nil
618 }
619
620 func (o *bannerService) getUserAssignedOrgs(ctx context.Context, username string) ([]*types.ExternalUserData, error) {
621 orgs := []*types.ExternalUserData{}
622 data := &types.GetExternalUserResponse{}
623 user := middleware.ForContext(ctx)
624 pageNumber := 0
625 client := o.BSLClient.WithUserTokenCredentials(ctx)
626 for !data.LastPage {
627 err := client.JSON(http.MethodGet, fmt.Sprintf(bspUserAssignedSubOrgsPath, username, pageNumber, pageSize), data)
628 if err != nil {
629 if apierror.IsNotFoundError(err) {
630 return []*types.ExternalUserData{}, nil
631 }
632 return nil, err
633 }
634
635 for _, org := range data.PageContent {
636 if org.OrganizationName == user.Organization && strings.Contains(username, commerceOrg) {
637 continue
638 }
639 if strings.Contains(org.OrganizationName, commerceOrg) {
640 continue
641 }
642 if !strings.Contains(org.OrganizationName, user.Organization) {
643 continue
644 }
645 orgs = append(orgs, org)
646 }
647 pageNumber++
648 }
649 return orgs, nil
650 }
651
652 func (o *bannerService) getUserAssignedEUs(ctx context.Context, username string) ([]*types.EnterpriseUnitData, error) {
653 var orgs []*types.EnterpriseUnitData
654 data := &types.FindEnterpriseUnitsResponse{}
655
656 client := o.BSLClient.WithUserTokenCredentials(ctx)
657
658 pageNumber := 0
659 for !data.LastPage {
660 err := client.JSON(http.MethodGet, fmt.Sprintf(bspGetAllAssignedOrgURL+bspPaging, username, pageNumber, pageSize), data)
661 if err != nil {
662 return nil, err
663 }
664 for _, eu := range data.PageContent {
665 if eu.EnterpriseUnitID == "" {
666 continue
667 }
668 userOrganization, err := getEnterpriseUnitData(client, eu.EnterpriseUnitID)
669 if err != nil {
670 return nil, err
671 }
672
673 if userOrganization.Active {
674 orgs = append(orgs, &types.EnterpriseUnitData{
675 Name: userOrganization.Name,
676 Description: userOrganization.Description,
677 EnterpriseUnitID: userOrganization.EnterpriseUnitID,
678 })
679 }
680 }
681 pageNumber++
682 }
683 return orgs, nil
684 }
685
686 func getEnterpriseUnitData(client *bsl.Request, enterpriseUnitID string) (*types.EnterpriseUnitData, error) {
687 data := &types.EnterpriseUnitData{}
688 return data, client.JSON(http.MethodGet, BspEnterpriseUnitPath+"/"+enterpriseUnitID, data)
689 }
690
691
692 func getAllEUs(client *bsl.Request) ([]*types.EnterpriseUnitData, error) {
693 var orgs []*types.EnterpriseUnitData
694 data := &types.FindEnterpriseUnitsResponse{}
695
696 pageNumber := 0
697 for pageNumber <= data.TotalPages {
698 err := client.
699 JSON(http.MethodGet, fmt.Sprintf(bspEnterpriseUnitOrganizationsPath+bspPaging, pageNumber, pageSize), data)
700 if err != nil {
701 return nil, err
702 }
703 orgs = append(orgs, data.PageContent...)
704 pageNumber++
705 }
706 return orgs, nil
707 }
708
709
710 func getAllSubOrgs(client *bsl.Request, parentOrg string) ([]*types.OrganizationViewData, error) {
711 var orgs []*types.OrganizationViewData
712 data := &types.GetOrganizationSubOrgsResponse{}
713
714 pageNumber := 0
715 for pageNumber <= data.TotalPages {
716 if data.LastPage {
717 break
718 }
719 err := client.JSON(http.MethodGet, fmt.Sprintf(bspSubOrganizationsPath, parentOrg, pageNumber, pageSize), data)
720 if err != nil {
721 return nil, err
722 }
723 orgs = append(orgs, data.PageContent...)
724 pageNumber++
725 }
726 return orgs, nil
727 }
728
729 func (o *bannerService) CreateBannerCr(ctx context.Context, uuid, projectID, name, enterpriseUnitID string, enablements []string) (string, error) {
730 user := middleware.ForContext(ctx)
731 bannerRequest := utils.NewBanner(uuid, name, bsl.GetOrgShortName(user.Organization), enterpriseUnitID, projectID, enablements)
732 if err := bannerRequest.IsValid(); err != nil {
733 return "", err
734 }
735 bannerRequestByte, err := json.Marshal(bannerRequest)
736 if err != nil {
737 return "", err
738 }
739 bannerRequestBase64 := utils.ToBase64(bannerRequestByte)
740 return bannerRequestBase64, nil
741 }
742
743 func (o *bannerService) CreateBannerSQLEntry(ctx context.Context, projectID, bannerName, bannerType, enterpriseUnitID, bannerUUID, description string) error {
744 var tenantID string
745 user := middleware.ForContext(ctx)
746 row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetTenantByOrgNameQuery, bsl.GetOrgShortName(user.Organization))
747 if err := row.Scan(&tenantID); err != nil {
748 return sqlerr.Wrap(err)
749 }
750 _, err := o.SQLDB.ExecContext(ctx, sqlquery.BannerInsertQuery, enterpriseUnitID, bannerName, bannerType, projectID, tenantID, bannerUUID, description)
751 if err != nil {
752 return sqlerr.Wrap(err)
753 }
754 return nil
755 }
756
757 func (o *bannerService) DeleteBannerSQLEntry(ctx context.Context, bannerUUID string) error {
758 _, err := o.SQLDB.ExecContext(ctx, sqlquery.DeleteBannerQuery, bannerUUID)
759 if err != nil {
760 return sqlerr.Wrap(err)
761 }
762 return nil
763 }
764
765 func (o *bannerService) CreateEUBanner(ctx context.Context, name string, description *string) (*model.Banner, error) {
766 desc := ""
767 if !utils.IsNullOrEmpty(description) {
768 desc = *description
769 }
770
771 eud := &types.EnterpriseUnitData{}
772 err := o.BSLClient.WithUserTokenCredentials(ctx).
773 SetPayload(mapper.ToEnterpriseUnitData("", name, desc, types.IsOrganization, true)).
774 JSON(http.MethodPost, BspEnterpriseUnitPath, eud)
775 if err != nil {
776 log.Err(err).Msg("error from bsp while creating an org")
777 return nil, err
778 }
779
780 return mapper.EnterpriseUnitToBanner(eud), nil
781 }
782
783 func (o *bannerService) CreateOrganizationBanner(ctx context.Context, name string, description *string) (*model.Banner, error) {
784 user := middleware.ForContext(ctx)
785
786 desc := ""
787 if !utils.IsNullOrEmpty(description) {
788 desc = *description
789 }
790
791 createOrg := &types.CreateOrganizationRequest{
792 Name: name,
793 Description: desc,
794 DisplayName: name + " Banner",
795 ParentOrganizationName: user.Organization,
796 }
797 orgResponse := &types.CreateOrganizationResponse{}
798 err := o.BSLClient.WithUserTokenCredentials(ctx).SetPayload(createOrg).JSON(http.MethodPost, bspOrgPath, orgResponse)
799 if err != nil {
800 log.Err(err).Msg("error from bsp while creating an org")
801 return nil, err
802 }
803
804 return mapper.ToOrgBanner(orgResponse), nil
805 }
806
807 func (o *bannerService) UndoCreateBanner(ctx context.Context, name, id, uuid, bannerObject string, isOrgBanner *bool, bucketPath string) error {
808 var err error
809 if isOrgBanner != nil && *isOrgBanner {
810 _, err = o.DeleteOrgBanner(ctx, id)
811 } else {
812 _, err = o.DeleteEUBanner(ctx, name, id)
813 }
814
815 deleteErr := o.DeleteBannerSQLEntry(ctx, uuid)
816 if deleteErr != nil {
817 if err != nil {
818 err = fmt.Errorf("%w, error deleting banner sql entry: %s", err, deleteErr)
819 } else {
820 err = fmt.Errorf("error deleting banner sql entry: %w", deleteErr)
821 }
822 }
823
824 deleteStoreClusterMessage := chariotClientApi.
825 NewChariotMessage().
826 SetOperation(chariotClientApi.Delete).
827 SetOwner(ComponentOwner).
828 SetBanner(o.TopLevelProjectID).
829 SetCluster(bucketPath).
830 AddObject(bannerObject)
831
832 if deleteChariotMsgErr := o.ChariotService.InvokeChariotPubsub(ctx, deleteStoreClusterMessage, nil); deleteChariotMsgErr != nil {
833 err = fmt.Errorf("%w, error calling chariot v2 and deleting a banner cr: %s", err, deleteChariotMsgErr)
834 } else {
835 err = fmt.Errorf("error calling chariot v2 and deleting a banner cr: %w", deleteChariotMsgErr)
836 }
837 return err
838 }
839
840 func (o *bannerService) DeleteEUBanner(ctx context.Context, name, id string) (bool, error) {
841 dname := name + "-deleted-" + time.Now().UTC().String()
842 payload := mapper.ToEnterpriseUnitData(id, dname, "organization deleted", "", false)
843 err := o.BSLClient.WithUserTokenCredentials(ctx).SetPayload(payload).Put(BspEnterpriseUnitPath)
844 if err != nil {
845 log.Err(err).Msg("error from bsp while deleting an org")
846 return false, err
847 }
848 return true, nil
849 }
850
851 func (o *bannerService) DeleteOrgBanner(ctx context.Context, id string) (bool, error) {
852 client := o.BSLClient.WithUserTokenCredentials(ctx)
853 err := client.Delete(fmt.Sprintf(bspDeleteOrgPath, id))
854 if err != nil {
855 log.Err(err).Msg("error from bsp while deleting an org")
856 return false, err
857 }
858 return true, nil
859 }
860
861 func (o *bannerService) AssignUserToEUBanners(ctx context.Context, username string, banners []string) error {
862 if err := o.clearAssignedEUBanners(ctx, username); err != nil {
863 return err
864 }
865 client := o.BSLClient.WithUserTokenCredentials(ctx)
866 for _, banner := range banners {
867 err := client.Put(fmt.Sprintf(bspEnterpriseUnitGrantBasePath, username, banner))
868 if err != nil {
869 return err
870 }
871 }
872
873 return nil
874 }
875
876
877 func (o *bannerService) AssignUserToOrgBanners(ctx context.Context, username string, banners []*model.Banner) error {
878 if err := o.clearAssignOrgBanners(ctx, username); err != nil {
879 return err
880 }
881
882 externalUser := &types.CreateExternalUserRequest{
883 Status: ActiveStatus,
884 Username: username,
885 }
886 client := o.BSLClient.WithUserTokenCredentials(ctx)
887 for _, banner := range banners {
888 err := client.SetOrgID(banner.BannerBSLId).SetPayload(externalUser).Post(bspExternalUserPath)
889 if err != nil {
890 return err
891 }
892 }
893 return nil
894 }
895
896 func (o *bannerService) GetUsersForEuBanner(ctx context.Context, bannerBSLID string) ([]*model.User, error) {
897 data := &types.FindUnitGrantsResponse{}
898 client := o.BSLClient.WithUserTokenCredentials(ctx)
899 var userNames []types.UserName
900 pageNumber := 0
901 for !data.LastPage {
902 err := client.JSON(http.MethodGet, fmt.Sprintf(bspGetOrgUsersListPath, bannerBSLID, pageNumber, pageSize), data)
903 if err != nil {
904 return nil, err
905 }
906 if data.TotalPages == 0 {
907 break
908 }
909 for _, grantViewData := range data.PageContent {
910 userNames = append(userNames, types.UserName{Username: grantViewData.Username})
911 }
912 pageNumber++
913 }
914
915 if len(userNames) == 0 {
916 return make([]*model.User, 0), nil
917 }
918
919 payload := types.GetUserDetailsRequest{UserIDs: userNames}
920 res := &types.GetUserDetailsResponse{}
921 err := client.SetPayload(payload).JSON(http.MethodPost, bspUserDetailsPath, res)
922
923 if err != nil {
924 return nil, err
925 }
926 return res.Users, nil
927 }
928
929 func (o *bannerService) clearAssignedEUBanners(ctx context.Context, username string) error {
930 data, err := o.getUserAssignedEUs(ctx, username)
931 if err != nil {
932 return err
933 }
934 client := o.BSLClient.WithUserTokenCredentials(ctx)
935 for _, eud := range data {
936 if eud.EnterpriseUnitID == "" {
937 continue
938 }
939 if err := revokeUser(client, username, eud.EnterpriseUnitID); err != nil {
940 return err
941 }
942 }
943 return nil
944 }
945
946 func revokeUser(client *bsl.Request, user string, organizationID string) error {
947 return client.Delete(fmt.Sprintf(bspEnterpriseUnitGrantBasePath, user, organizationID))
948 }
949
950 func (o *bannerService) clearAssignOrgBanners(ctx context.Context, username string) error {
951 orgs, err := o.getUserAssignedOrgs(ctx, username)
952 if err != nil {
953 return err
954 }
955 client := o.BSLClient.WithUserTokenCredentials(ctx)
956 for _, org := range orgs {
957 if org.Primary {
958 continue
959 }
960 if err := o.deleteExternalUser(client, username, org.OrganizationName); err != nil {
961 return err
962 }
963 }
964 return nil
965 }
966
967 func (o *bannerService) deleteExternalUser(client *bsl.Request, username, org string) error {
968 return client.SetOrg(org).Delete(fmt.Sprintf("%s/%s", bspUsersPath, username))
969 }
970
971 func (o *bannerService) DeleteClusterSQLEntry(ctx context.Context, clusterEdgeID string) error {
972 _, err := o.SQLDB.ExecContext(ctx, sqlquery.DeleteClusterQuery, clusterEdgeID)
973 if err != nil {
974 return sqlerr.Wrap(err)
975 }
976 return nil
977 }
978
979 func NewBannerService(gkeService GkeClient, chariotService ChariotService, bslClient *bsl.Client, project string, sqlDB *sql.DB, config *apiTypes.Config) BannerService {
980 return &bannerService{
981 GkeService: gkeService,
982 ChariotService: chariotService,
983 BSLClient: bslClient,
984 SQLDB: sqlDB,
985 TopLevelProjectID: project,
986 config: config,
987 }
988 }
989
View as plain text