Skip to content

Commit 9e879e5

Browse files
committed
Tenant filter by Privileged Sites
1 parent 8a9495c commit 9e879e5

12 files changed

Lines changed: 187 additions & 190 deletions

File tree

rest-api/api/pkg/api/handler/expectedmachine.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func ValidateProviderOrTenantSiteAccess(ctx context.Context, logger zerolog.Logg
6060
// Privileged tenants may access Sites via a Ready TenantAccount with
6161
// TargetedInstanceCreation even without an explicit TenantSite row.
6262
if !hasAccess {
63-
enabled, err := common.EffectiveTargetedInstanceCreation(ctx, nil, dbSession, tenant, site.ID)
63+
enabled, err := common.TenantHasTargetedInstanceCreation(ctx, nil, dbSession, tenant, site)
6464
if err != nil {
6565
logger.Error().Err(err).Msg("error resolving TargetedInstanceCreation for Tenant/Site")
6666
return false, cutil.NewAPIError(http.StatusInternalServerError, "Failed to resolve Tenant capability for Site due to DB error", nil)
@@ -347,7 +347,7 @@ func (gaemh GetAllExpectedMachineHandler) Handle(c echo.Context) error {
347347
}
348348

349349
if tenant != nil {
350-
privileged, err := common.TenantHasTargetedInstanceCreation(ctx, nil, gaemh.dbSession, tenant)
350+
privileged, err := common.TenantHasTargetedInstanceCreation(ctx, nil, gaemh.dbSession, tenant, nil)
351351
if err != nil {
352352
logger.Error().Err(err).Msg("error resolving TargetedInstanceCreation for Tenant")
353353
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to resolve Tenant capability due to DB error", nil)

rest-api/api/pkg/api/handler/instance.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -892,12 +892,12 @@ func (cih CreateInstanceHandler) Handle(c echo.Context) error {
892892

893893
// Begin validating Machine ID
894894
if apiRequest.MachineID != nil {
895-
enabledForSite, derr := common.EffectiveTargetedInstanceCreation(ctx, tx, cih.dbSession, tenant, site.ID)
895+
privilegedAccess, derr := common.TenantHasTargetedInstanceCreation(ctx, tx, cih.dbSession, tenant, site)
896896
if derr != nil {
897897
logger.Error().Err(derr).Msg("error checking effective targeted instance creation for Site")
898898
return cutil.NewAPIError(http.StatusInternalServerError, "Failed to verify capability for Site", nil)
899899
}
900-
if !enabledForSite {
900+
if !privilegedAccess {
901901
logger.Warn().Msg("tenant does not have capability to create instances from specific machine")
902902
return cutil.NewAPIError(http.StatusForbidden, "Tenant does not have capability to create Instances using specific Machine ID", nil)
903903
}
@@ -4905,7 +4905,10 @@ func (dih DeleteInstanceHandler) Handle(c echo.Context) error {
49054905
// capability. By the time `ToProto` runs the request is safe
49064906
// to trust.
49074907
if apiRequest.IsRepairTenant != nil && *apiRequest.IsRepairTenant {
4908-
enabledForSite, derr := common.EffectiveTargetedInstanceCreation(ctx, tx, dih.dbSession, instance.Tenant, instance.SiteID)
4908+
enabledForSite, derr := common.TenantHasTargetedInstanceCreation(ctx, tx, dih.dbSession, instance.Tenant, &cdbm.Site{
4909+
ID: instance.SiteID,
4910+
InfrastructureProviderID: instance.InfrastructureProviderID,
4911+
})
49094912
if derr != nil {
49104913
logger.Error().Err(derr).Msg("error checking effective targeted instance creation for Instance's Site")
49114914
return cutil.NewAPIError(http.StatusInternalServerError, "Failed to verify capability for Instance's Site", nil)

rest-api/api/pkg/api/handler/machine.go

Lines changed: 29 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,8 @@ func (gamh GetAllMachineHandler) Handle(c echo.Context) error {
206206
// request the privileged-tenant pre-gate here (requirePrivilegedTenant=false):
207207
// that gate keys off a Ready TenantAccount without site context and would
208208
// reject site-privileged tenants (or those whose privilege is per-site)
209-
// before machine.SiteID is known. The site-scoped
210-
// EffectiveTargetedInstanceCreation checks below are authoritative and
211-
// filter results to the providers/sites where the capability is effective,
212-
// yielding an empty list rather than a spurious 403.
209+
// before siteId is known. Privileged Tenant access is resolved below via
210+
// GetPrivilegedAccessSiteIDsForTenant, which honors per-site overrides.
213211
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, gamh.dbSession, org, dbUser, true, false)
214212
if apiError != nil {
215213
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
@@ -238,35 +236,27 @@ func (gamh GetAllMachineHandler) Handle(c echo.Context) error {
238236
return cutil.NewAPIErrorResponse(c, http.StatusBadRequest, errMsg, nil)
239237
}
240238

241-
filterInput := cdbm.MachineFilterInput{
242-
InfrastructureProviderIDs: []uuid.UUID{},
243-
}
239+
filterInput := cdbm.MachineFilterInput{}
244240

245-
// Validate other query params
246241
if infrastructureProvider != nil {
247-
filterInput.InfrastructureProviderIDs = append(filterInput.InfrastructureProviderIDs, infrastructureProvider.ID)
242+
filterInput.InfrastructureProviderIDs = []uuid.UUID{infrastructureProvider.ID}
248243
}
249244

250-
var tenantAccountProviderIDs []uuid.UUID
245+
var privilegedSiteIDs []uuid.UUID
251246
if tenant != nil {
252-
taDAO := cdbm.NewTenantAccountDAO(gamh.dbSession)
253-
tas, _, serr := taDAO.GetAll(ctx, nil, cdbm.TenantAccountFilterInput{
254-
TenantIDs: []uuid.UUID{tenant.ID},
255-
Statuses: []string{cdbm.TenantAccountStatusReady},
256-
}, cdbp.PageInput{Limit: cutil.GetPtr(cdbp.TotalLimit)}, []string{})
247+
var serr error
248+
privilegedSiteIDs, serr = common.GetPrivilegedAccessSiteIDsForTenant(ctx, nil, gamh.dbSession, tenant)
257249
if serr != nil {
258-
logger.Error().Err(serr).Msg("error retrieving Tenant Accounts for Tenant")
259-
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Error retrieving Tenant Accounts for Tenant", nil)
250+
logger.Error().Err(serr).Msg("error resolving privileged Site access for Tenant")
251+
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to verify privileges for Tenant", nil)
260252
}
261-
262-
for _, ta := range tas {
263-
tenantAccountProviderIDs = append(tenantAccountProviderIDs, ta.InfrastructureProviderID)
253+
if len(privilegedSiteIDs) == 0 {
254+
return cutil.NewAPIErrorResponse(c, http.StatusForbidden, "Tenant does not have Targeted Instance Creation capability enabled for any Site", nil)
264255
}
265256
}
266257

267258
// Validate site id if provided
268259
qSiteID := c.QueryParam("siteId")
269-
var filterSite *cdbm.Site
270260
if qSiteID != "" {
271261
site, serr := common.GetSiteFromIDString(ctx, nil, qSiteID, gamh.dbSession)
272262
if serr != nil {
@@ -276,71 +266,22 @@ func (gamh GetAllMachineHandler) Handle(c echo.Context) error {
276266
logger.Error().Err(serr).Str("Site ID", qSiteID).Msg("error retrieving Site specified in query")
277267
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Error retrieving Site specified in query", nil)
278268
}
279-
filterSite = site
280269

281-
isAssociated := false
282270
if infrastructureProvider != nil {
283-
// Check if Site belongs to org's Infrastructure Provider
284-
if site.InfrastructureProviderID == infrastructureProvider.ID {
285-
isAssociated = true
271+
if site.InfrastructureProviderID != infrastructureProvider.ID {
272+
logger.Error().Msg("Site is not associated with org")
273+
return cutil.NewAPIErrorResponse(c, http.StatusForbidden, "Current org is not associated with the Site specified in query", nil)
286274
}
287-
}
288-
289-
if !isAssociated && tenant != nil {
290-
isAssociated = slices.Contains(tenantAccountProviderIDs, site.InfrastructureProviderID)
291-
}
292-
293-
if isAssociated {
294275
filterInput.SiteIDs = []uuid.UUID{site.ID}
295276
} else {
296-
logger.Error().Msg("Site is not associated with org")
297-
return cutil.NewAPIErrorResponse(c, http.StatusForbidden, "Current org is not associated with the Site specified in query", nil)
298-
}
299-
}
300-
301-
if tenant != nil {
302-
if filterSite != nil {
303-
enabledForSite, serr := common.EffectiveTargetedInstanceCreation(ctx, nil, gamh.dbSession, tenant, filterSite.ID)
304-
if serr != nil {
305-
logger.Error().Err(serr).Msg("error checking effective targeted instance creation for Site")
306-
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to verify privileges for Site", nil)
307-
}
308-
if enabledForSite {
309-
filterInput.InfrastructureProviderIDs = append(filterInput.InfrastructureProviderIDs, filterSite.InfrastructureProviderID)
310-
}
311-
} else {
312-
siteDAO := cdbm.NewSiteDAO(gamh.dbSession)
313-
privilegedProviderIDs := make(map[uuid.UUID]struct{})
314-
for _, providerID := range tenantAccountProviderIDs {
315-
if _, listed := privilegedProviderIDs[providerID]; listed {
316-
continue
317-
}
318-
319-
sites, _, serr := siteDAO.GetAll(ctx, nil, cdbm.SiteFilterInput{
320-
InfrastructureProviderIDs: []uuid.UUID{providerID},
321-
}, cdbp.PageInput{Limit: cutil.GetPtr(cdbp.TotalLimit)}, nil)
322-
if serr != nil {
323-
logger.Error().Err(serr).Msg("error retrieving Sites for privileged Tenant")
324-
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Error retrieving Sites for privileged Tenant", nil)
325-
}
326-
327-
for _, site := range sites {
328-
enabledForSite, serr := common.EffectiveTargetedInstanceCreation(ctx, nil, gamh.dbSession, tenant, site.ID)
329-
if serr != nil {
330-
logger.Error().Err(serr).Msg("error checking effective targeted instance creation for Site")
331-
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to verify privileges for Site", nil)
332-
}
333-
if enabledForSite {
334-
privilegedProviderIDs[providerID] = struct{}{}
335-
break
336-
}
337-
}
338-
}
339-
340-
for providerID := range privilegedProviderIDs {
341-
filterInput.InfrastructureProviderIDs = append(filterInput.InfrastructureProviderIDs, providerID)
277+
if !slices.Contains(privilegedSiteIDs, site.ID) {
278+
logger.Error().Msg("Site is not associated with org")
279+
return cutil.NewAPIErrorResponse(c, http.StatusForbidden, "Current org is not associated with the Site specified in query", nil)
342280
}
281+
filterInput.SiteIDs = []uuid.UUID{site.ID}
343282
}
283+
} else if tenant != nil {
284+
filterInput.SiteIDs = privilegedSiteIDs
344285
}
345286

346287
// Validate InstanceType ID if provided
@@ -665,7 +606,10 @@ func (gmh GetMachineHandler) Handle(c echo.Context) error {
665606
isAssociated = true
666607
isProviderOrPrivilegedTenant = true
667608
} else if tenant != nil {
668-
enabledForSite, serr := common.EffectiveTargetedInstanceCreation(ctx, nil, gmh.dbSession, tenant, machine.SiteID)
609+
enabledForSite, serr := common.TenantHasTargetedInstanceCreation(ctx, nil, gmh.dbSession, tenant, &cdbm.Site{
610+
ID: machine.SiteID,
611+
InfrastructureProviderID: machine.InfrastructureProviderID,
612+
})
669613
if serr != nil {
670614
logger.Error().Err(serr).Msg("error checking effective targeted instance creation for Machine's Site")
671615
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Error verifying capability for Machine's Site", nil)
@@ -766,7 +710,7 @@ func (umh UpdateMachineHandler) Handle(c echo.Context) error {
766710
// pre-gate here (requirePrivilegedTenant=false): that gate keys off the
767711
// legacy tenant-level flag and would reject TenantAccount-privileged
768712
// tenants before machine.SiteID is known. The site-scoped
769-
// EffectiveTargetedInstanceCreation check is the authoritative decision.
713+
// TenantHasTargetedInstanceCreation check is the authoritative decision.
770714
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, umh.dbSession, org, dbUser, false, false)
771715
if apiError != nil {
772716
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
@@ -807,7 +751,10 @@ func (umh UpdateMachineHandler) Handle(c echo.Context) error {
807751
// Validate if Tenant is allowed to update Machine
808752
if tenant != nil {
809753
// Check if Tenant is privileged
810-
enabledForSite, serr := common.EffectiveTargetedInstanceCreation(ctx, nil, umh.dbSession, tenant, machine.SiteID)
754+
enabledForSite, serr := common.TenantHasTargetedInstanceCreation(ctx, nil, umh.dbSession, tenant, &cdbm.Site{
755+
ID: machine.SiteID,
756+
InfrastructureProviderID: machine.InfrastructureProviderID,
757+
})
811758
if serr != nil {
812759
logger.Error().Err(serr).Msg("error checking effective targeted instance creation for Machine's Site")
813760
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Error verifying capability for Machine's Site, DB error", nil)

rest-api/api/pkg/api/handler/machine_test.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,15 +1057,12 @@ func TestMachineHandler_GetAll(t *testing.T) {
10571057
verifyChildSpanner: true,
10581058
},
10591059
{
1060-
name: "success case when Tenant has TargetedInstanceCreation capability and filters by Site ID",
1060+
name: "failure case when Tenant has no effective TargetedInstanceCreation for Site",
10611061
reqOrgName: tnOrg2,
10621062
user: tnu2,
10631063
querySiteID: cutil.GetPtr(siteT2.ID.String()),
1064-
expectedErr: false,
1065-
expectedStatus: http.StatusOK,
1066-
expectedCnt: 0,
1067-
expectedTotal: cutil.GetPtr(0),
1068-
expectInstance: false,
1064+
expectedErr: true,
1065+
expectedStatus: http.StatusForbidden,
10691066
verifyChildSpanner: true,
10701067
},
10711068
{
@@ -1078,13 +1075,11 @@ func TestMachineHandler_GetAll(t *testing.T) {
10781075
expectedTotal: cutil.GetPtr(totalCount / 2),
10791076
},
10801077
{
1081-
name: "empty result when Tenant has TargetedInstanceCreation capability but no Tenant Account",
1078+
name: "failure case when Tenant has no effective TargetedInstanceCreation on any Site",
10821079
reqOrgName: tnOrg8,
10831080
user: tnu8,
1084-
expectedErr: false,
1085-
expectedStatus: http.StatusOK,
1086-
expectedCnt: 0,
1087-
expectedTotal: cutil.GetPtr(0),
1081+
expectedErr: true,
1082+
expectedStatus: http.StatusForbidden,
10881083
},
10891084
{
10901085
name: "success case when Instance Type ID specified in query",

rest-api/api/pkg/api/handler/site.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ func (gsh GetSiteHandler) Handle(c echo.Context) error {
651651
}
652652

653653
if !isAssociated {
654-
enabled, serr := common.EffectiveTargetedInstanceCreation(ctx, nil, gsh.dbSession, tenant, stID)
654+
enabled, serr := common.TenantHasTargetedInstanceCreation(ctx, nil, gsh.dbSession, tenant, st)
655655
if serr != nil {
656656
logger.Error().Err(serr).Msg("error resolving TargetedInstanceCreation for Tenant/Site")
657657
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to resolve Tenant capability for Site, DB error", nil)
@@ -888,7 +888,7 @@ func (gash GetAllSiteHandler) Handle(c echo.Context) error {
888888

889889
// If Tenant has TargetedInstanceCreation on any Ready TenantAccount,
890890
// also retrieve all Sites from Providers where that capability is enabled.
891-
privileged, serr := common.TenantHasTargetedInstanceCreation(ctx, nil, gash.dbSession, tenant)
891+
privileged, serr := common.TenantHasTargetedInstanceCreation(ctx, nil, gash.dbSession, tenant, nil)
892892
if serr != nil {
893893
logger.Error().Err(serr).Msg("error resolving TargetedInstanceCreation for Tenant")
894894
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to resolve Tenant capability, DB error", nil)
@@ -1226,7 +1226,7 @@ func (gssdh GetSiteStatusDetailsHandler) Handle(c echo.Context) error {
12261226
}
12271227

12281228
if !isAssociated {
1229-
enabled, serr := common.EffectiveTargetedInstanceCreation(ctx, nil, gssdh.dbSession, tenant, stID)
1229+
enabled, serr := common.TenantHasTargetedInstanceCreation(ctx, nil, gssdh.dbSession, tenant, st)
12301230
if serr != nil {
12311231
logger.Error().Err(serr).Msg("error resolving TargetedInstanceCreation for Tenant/Site")
12321232
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to resolve Tenant capability for Site, DB error", nil)

rest-api/api/pkg/api/handler/sku.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func (gash GetAllSkuHandler) Handle(c echo.Context) error {
103103
return cutil.NewAPIErrorResponse(c, http.StatusForbidden, "Site specified in request data is does not belong to current org", nil)
104104
}
105105
} else if tenant != nil {
106-
enabled, err := common.EffectiveTargetedInstanceCreation(ctx, nil, gash.dbSession, tenant, site.ID)
106+
enabled, err := common.TenantHasTargetedInstanceCreation(ctx, nil, gash.dbSession, tenant, site)
107107
if err != nil {
108108
logger.Error().Err(err).Msg("error resolving TargetedInstanceCreation for Tenant/Site")
109109
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to resolve Tenant capability for Site due to DB error", nil)
@@ -260,7 +260,7 @@ func (gsh GetSkuHandler) Handle(c echo.Context) error {
260260
return cutil.NewAPIErrorResponse(c, http.StatusForbidden, "SKU does not belong to a Site owned by current org", nil)
261261
}
262262
} else if tenant != nil {
263-
enabled, err := common.EffectiveTargetedInstanceCreation(ctx, nil, gsh.dbSession, tenant, site.ID)
263+
enabled, err := common.TenantHasTargetedInstanceCreation(ctx, nil, gsh.dbSession, tenant, site)
264264
if err != nil {
265265
logger.Error().Err(err).Msg("error resolving TargetedInstanceCreation for Tenant/Site")
266266
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to resolve Tenant capability for Site due to DB error", nil)

rest-api/api/pkg/api/handler/tenant.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ func (gcth GetCurrentTenantHandler) Handle(c echo.Context) error {
207207
}
208208

209209
// Create response
210-
hasTargetedInstanceCreation, err := common.TenantHasTargetedInstanceCreation(ctx, nil, gcth.dbSession, tn)
210+
hasTargetedInstanceCreation, err := common.TenantHasTargetedInstanceCreation(ctx, nil, gcth.dbSession, tn, nil)
211211
if err != nil {
212212
logger.Error().Err(err).Msg("error resolving TargetedInstanceCreation for Tenant")
213213
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to resolve Tenant capability due to DB error", nil)

rest-api/api/pkg/api/handler/tenantaccount.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,8 +576,8 @@ func (gtah GetTenantAccountHandler) Handle(c echo.Context) error {
576576
TenantIDs: []uuid.UUID{*ta.TenantID},
577577
}, cdbp.PageInput{Limit: cutil.GetPtr(cdbp.TotalLimit)}, []string{"Site"})
578578
if err != nil {
579-
logger.Error().Err(err).Msg("error retrieving Tenant Sites for Tenant Account")
580-
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Tenant Sites for Tenant Account", nil)
579+
logger.Error().Err(err).Msg("error retrieving Tenant Sites for Tenant")
580+
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Tenant Sites for Tenant", nil)
581581
}
582582
}
583583

0 commit comments

Comments
 (0)