Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
df1ea9e
feat: manage TargetedInstanceCreation via TenantAccount siteCapabilities
hwadekar-nv Jul 1, 2026
b406e30
Fixed test
hwadekar-nv Jul 1, 2026
8b806c0
Removed function and moved inline
hwadekar-nv Jul 7, 2026
f716e36
Addressed review comment by cr
hwadekar-nv Jul 8, 2026
8a852f3
Fixed failing test cases
hwadekar-nv Jul 8, 2026
32990fa
reneraged openapi
hwadekar-nv Jul 8, 2026
6747449
added list of APIs in ingnoring paths
hwadekar-nv Jul 8, 2026
db5ebca
Removed config from Tenant model
hwadekar-nv Jul 8, 2026
2ddf1fb
fix lint and vet
hwadekar-nv Jul 8, 2026
fc6372a
Fix suggested by cr
hwadekar-nv Jul 8, 2026
001c94e
Tenant filter by Privileged Sites
hwadekar-nv Jul 9, 2026
b853bb4
Fix suggested by cr
hwadekar-nv Jul 9, 2026
3c838c1
Revert the changes by old version openapi sdk
hwadekar-nv Jul 10, 2026
ec2c268
Fixed suggestion by CR
hwadekar-nv Jul 13, 2026
a5fab40
Updated migration addressed legacy check
hwadekar-nv Jul 13, 2026
0af30a1
Addressed comments from review
hwadekar-nv Jul 14, 2026
8b2dbf5
Removed inline functions
hwadekar-nv Jul 14, 2026
eaf159a
fmt fix
hwadekar-nv Jul 14, 2026
5f5a761
Fixed the auth based on Site or Provider scope
hwadekar-nv Jul 15, 2026
2399979
fmt issue
hwadekar-nv Jul 15, 2026
b2a05a2
fix(rest-api): honor site TIC overrides in auth, tighten invite/PATCH…
hwadekar-nv Jul 16, 2026
a97f0ff
fix(rest-api): address CodeRabbit follow-ups for siteCapabilities pre…
hwadekar-nv Jul 16, 2026
51df2b9
Updated config allow nil for Tenant
hwadekar-nv Jul 17, 2026
985eb74
Revised evaluation of Tenant privilege
thossain-nv Jul 17, 2026
f3502b6
Revise function to retrieve privileged Site IDs
thossain-nv Jul 17, 2026
5696e1a
Updated test case based on updated auth model
hwadekar-nv Jul 20, 2026
dd10305
Improve reusability and removed inline functions
hwadekar-nv Jul 20, 2026
b7f6d31
Remove scope from Tenant Account site capabilities
hwadekar-nv Jul 21, 2026
ca8c2e7
fix(rest-api): address tenant capability authorization review findings
hwadekar-nv Jul 22, 2026
d9b6241
docs(rest-api): clarify tenant site capability validation and replace…
hwadekar-nv Jul 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions rest-api/api/pkg/api/handler/allocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ func (gaah GetAllAllocationHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusBadRequest, errStr, nil)
}

provider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, gaah.dbSession, org, dbUser, true, false)
provider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, gaah.dbSession, org, dbUser, true, nil)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}
Expand Down Expand Up @@ -860,7 +860,7 @@ func (gah GetAllocationHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusBadRequest, errStr, nil)
}

provider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, gah.dbSession, org, dbUser, true, false)
provider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, gah.dbSession, org, dbUser, true, nil)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}
Expand Down
15 changes: 10 additions & 5 deletions rest-api/api/pkg/api/handler/bmcreset.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,6 @@ func (h ResetMachineBMCHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusBadRequest, "Failed to parse request data, potentially invalid structure", nil)
}

provider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, h.dbSession, org, dbUser, false, true)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

machine, err := cdbm.NewMachineDAO(h.dbSession).GetByID(ctx, nil, machineID, []string{cdbm.SiteRelationName}, false)
if err != nil {
if errors.Is(err, cdb.ErrDoesNotExist) {
Expand All @@ -98,6 +93,16 @@ func (h ResetMachineBMCHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Machine details, DB error", nil)
}

// Scope the Tenant TargetedInstanceCreation capability check to the target
// Machine's Provider and Site so privilege is verified where the operation
// lands, not against some unrelated Site the Tenant happens to be privileged on.
provider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, h.dbSession, org, dbUser, false, &common.TenantPrivilegeScope{
SiteID: &machine.SiteID,
})
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

isAssociated := false
if provider != nil {
isAssociated = machine.InfrastructureProviderID == provider.ID
Expand Down
15 changes: 10 additions & 5 deletions rest-api/api/pkg/api/handler/dpureprovision.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,6 @@ func (h ReprovisionMachineDpuHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusBadRequest, err.Error(), nil)
}

provider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, h.dbSession, org, dbUser, false, true)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

machine, err := cdbm.NewMachineDAO(h.dbSession).GetByID(ctx, nil, machineID, []string{cdbm.SiteRelationName}, false)
if err != nil {
if errors.Is(err, cdb.ErrDoesNotExist) {
Expand All @@ -102,6 +97,16 @@ func (h ReprovisionMachineDpuHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Machine details, DB error", nil)
}

// Scope the Tenant TargetedInstanceCreation capability check to the target
// Machine's Provider and Site so privilege is verified where the operation
// lands, not against some unrelated Site the Tenant happens to be privileged on.
provider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, h.dbSession, org, dbUser, false, &common.TenantPrivilegeScope{
SiteID: &machine.SiteID,
})
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

isAssociated := false
if provider != nil {
isAssociated = machine.InfrastructureProviderID == provider.ID
Expand Down
9 changes: 1 addition & 8 deletions rest-api/api/pkg/api/handler/dpureprovision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,12 @@ func TestReprovisionMachineDpuHandlerAllowsPrivilegedTenant(t *testing.T) {
tenantOrg := "test-tenant-org"
tenantUser := common.TestBuildUser(t, fixture.DBSession, "test-tenant-starfleet-id", tenantOrg, []string{authz.TenantAdminRole})
tenant := common.TestBuildTenant(t, fixture.DBSession, "test-tenant", tenantOrg, tenantUser)
tenant, err := cdbm.NewTenantDAO(fixture.DBSession).Update(ctx, nil, cdbm.TenantUpdateInput{
TenantID: tenant.ID,
Config: &cdbm.TenantConfig{
TargetedInstanceCreation: true,
},
})
require.NoError(t, err)

machine, err := cdbm.NewMachineDAO(fixture.DBSession).GetByID(ctx, nil, fixture.MachineID, []string{}, false)
require.NoError(t, err)
provider, err := cdbm.NewInfrastructureProviderDAO(fixture.DBSession).GetByID(ctx, nil, machine.InfrastructureProviderID, []string{})
require.NoError(t, err)
common.TestBuildTenantAccount(t, fixture.DBSession, provider, &tenant.ID, tenant.Org, cdbm.TenantAccountStatusReady, tenantUser)
common.TestBuildTenantAccountWithTargetedInstanceCreation(t, fixture.DBSession, provider, &tenant.ID, tenant.Org, cdbm.TenantAccountStatusReady, tenantUser)

fixture.Org = tenantOrg
fixture.User = tenantUser
Expand Down
152 changes: 65 additions & 87 deletions rest-api/api/pkg/api/handler/expectedmachine.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,42 +36,17 @@ import (

// ValidateProviderOrTenantSiteAccess validates if the provider or tenant has access to the site
func ValidateProviderOrTenantSiteAccess(ctx context.Context, logger zerolog.Logger, dbSession *cdb.Session, site *cdbm.Site, infrastructureProvider *cdbm.InfrastructureProvider, tenant *cdbm.Tenant) (bool, *cutil.APIError) {
hasAccess := false
hasAccess := infrastructureProvider != nil && site.InfrastructureProviderID == infrastructureProvider.ID

// Validate if Provider has access to the Site
if infrastructureProvider != nil && site.InfrastructureProviderID == infrastructureProvider.ID {
hasAccess = true
}

if !hasAccess && tenant != nil {
// Check Tenant Site relationship
tsDAO := cdbm.NewTenantSiteDAO(dbSession)
_, tsCount, err := tsDAO.GetAll(ctx, nil, cdbm.TenantSiteFilterInput{
TenantIDs: []uuid.UUID{tenant.ID},
SiteIDs: []uuid.UUID{site.ID},
}, paginator.PageInput{}, []string{})
if tenant != nil {
// Effective TargetedInstanceCreation for this Site governs tenant access.
// A TenantSite association alone must not bypass an explicit false override.
enabled, err := common.TenantHasTargetedInstanceCreation(ctx, nil, dbSession, tenant, &common.TenantPrivilegeScope{SiteID: &site.ID})
if err != nil {
logger.Error().Err(err).Msg("error retrieving Tenant Site relationship")
return false, cutil.NewAPIError(http.StatusInternalServerError, "Failed to check Tenant/Site association due to DB error", nil)
}

hasAccess = tsCount > 0

// Check if Tenant is privileged
if !hasAccess && tenant.Config.TargetedInstanceCreation {
// Check if privileged tenant has an account with the Site's Infrastructure Provider
taDAO := cdbm.NewTenantAccountDAO(dbSession)
_, taCount, err := taDAO.GetAll(ctx, nil, cdbm.TenantAccountFilterInput{
InfrastructureProviderID: &site.InfrastructureProviderID,
TenantIDs: []uuid.UUID{tenant.ID},
}, paginator.PageInput{}, []string{})
if err != nil {
logger.Error().Err(err).Msg("error retrieving Tenant Account for Site")
return false, cutil.NewAPIError(http.StatusInternalServerError, "Failed to retrieve Tenant's Account with Site's Provider due to DB error", nil)
}

hasAccess = taCount > 0
logger.Error().Err(err).Msg("error resolving TargetedInstanceCreation for Tenant/Site")
return false, cutil.NewAPIError(http.StatusInternalServerError, "Failed to resolve Tenant capability for Site due to DB error", nil)
}
hasAccess = hasAccess || enabled
}

return hasAccess, nil
Expand Down Expand Up @@ -119,12 +94,6 @@ func (cemh CreateExpectedMachineHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve current user", nil)
}

// ensure our user is a provider or tenant for the org
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, cemh.dbSession, org, dbUser, false, true)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Validate request
// Bind request data to API model
apiRequest := model.APIExpectedMachineCreateRequest{}
Expand Down Expand Up @@ -168,6 +137,12 @@ func (cemh CreateExpectedMachineHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Site specified in request data due to DB error", nil)
}

// Scope tenant privilege to the Site targeted by this request.
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, cemh.dbSession, org, dbUser, false, &common.TenantPrivilegeScope{SiteID: &site.ID})
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Validate ProviderTenantSite relationship and site state
hasAccess, apiError := ValidateProviderOrTenantSiteAccess(ctx, logger, cemh.dbSession, site, infrastructureProvider, tenant)
if apiError != nil {
Expand Down Expand Up @@ -293,7 +268,7 @@ func NewGetAllExpectedMachineHandler(dbSession *cdb.Session, cfg *config.Config)

// Handle godoc
// @Summary Get all ExpectedMachines
// @Description Get all ExpectedMachines
// @Description Get all ExpectedMachines. Tenant results are restricted to Sites with effective TargetedInstanceCreation; no single-Site privilege scope is required.
// @Tags ExpectedMachine
// @Accept json
// @Produce json
Expand All @@ -317,13 +292,18 @@ func (gaemh GetAllExpectedMachineHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve current user", nil)
}

// ensure our user is a provider for the org
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, gaemh.dbSession, org, dbUser, true, true)
// ensure our user is a provider or tenant for the org. We do not request the
// privileged-tenant pre-gate (requirePrivilegedScope=nil): tenant-only
// callers are scoped below to Sites with effective TargetedInstanceCreation,
// and receive 403 when none are resolved.
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, gaemh.dbSession, org, dbUser, true, nil)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

filterInput := cdbm.ExpectedMachineFilterInput{}
// Initialize SiteIDs to a non-nil empty slice so an unscoped caller (e.g. a
// non-privileged Tenant) matches no Sites instead of every Site.
filterInput := cdbm.ExpectedMachineFilterInput{SiteIDs: []uuid.UUID{}}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if infrastructureProvider != nil {
// Get all Sites for the org's Infrastructure Provider
Expand All @@ -346,20 +326,18 @@ func (gaemh GetAllExpectedMachineHandler) Handle(c echo.Context) error {
}

if tenant != nil {
// Check if Tenant is privileged
if tenant.Config.TargetedInstanceCreation {
// Get IDs for all Sites the privileged Tenant has an access with
tenantSiteDAO := cdbm.NewTenantSiteDAO(gaemh.dbSession)
tenantSites, _, err := tenantSiteDAO.GetAll(ctx, nil, cdbm.TenantSiteFilterInput{TenantIDs: []uuid.UUID{tenant.ID}}, paginator.PageInput{Limit: cutil.GetPtr(math.MaxInt)}, nil)
if err != nil {
logger.Error().Err(err).Msg("error retrieving Tenant Sites from DB")
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Tenant Sites due to DB error", nil)
}

for _, tenantSite := range tenantSites {
filterInput.SiteIDs = append(filterInput.SiteIDs, tenantSite.SiteID)
}
// Scope tenant listing to Sites where the Tenant has effective
// TargetedInstanceCreation, honoring per-site TenantSite.config overrides.
privilegedSiteIDs, err := common.GetPrivilegedAccessSiteIDsForTenant(ctx, nil, gaemh.dbSession, tenant)
if err != nil {
logger.Error().Err(err).Msg("error resolving privileged Site access for Tenant")
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to resolve Tenant capability due to DB error", nil)
}
filterInput.SiteIDs = append(filterInput.SiteIDs, privilegedSiteIDs...)
}

if infrastructureProvider == nil && tenant != nil && len(filterInput.SiteIDs) == 0 {
return cutil.NewAPIErrorResponse(c, http.StatusForbidden, "Tenant does not have Targeted Instance Creation capability enabled for any Site", nil)
}

siteIDStr := c.QueryParam("siteId")
Expand All @@ -383,7 +361,7 @@ func (gaemh GetAllExpectedMachineHandler) Handle(c echo.Context) error {
}

if !isAssociated && tenant != nil {
// We've already populated the filter with Providers the Tenant has an account with
// filterInput.SiteIDs already holds the Tenant's effective privileged Sites.
isAssociated = slices.Contains(filterInput.SiteIDs, site.ID)
}

Expand Down Expand Up @@ -498,12 +476,6 @@ func (gemh GetExpectedMachineHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve current user", nil)
}

// ensure our user is a provider for the org
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, gemh.dbSession, org, dbUser, true, true)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Get Expected Machine ID from URL param
expectedMachineIDStr := c.Param("id")
expectedMachineID, err := uuid.Parse(expectedMachineIDStr)
Expand Down Expand Up @@ -545,6 +517,12 @@ func (gemh GetExpectedMachineHandler) Handle(c echo.Context) error {
}
}

// Scope tenant privilege to the Expected Machine's Site.
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, gemh.dbSession, org, dbUser, true, &common.TenantPrivilegeScope{SiteID: &site.ID})
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Validate ProviderTenantSite relationship and site state
hasAccess, apiError := ValidateProviderOrTenantSiteAccess(ctx, logger, gemh.dbSession, site, infrastructureProvider, tenant)
if apiError != nil {
Expand Down Expand Up @@ -606,12 +584,6 @@ func (uemh UpdateExpectedMachineHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve current user", nil)
}

// Ensure our user is a provider for the org
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, uemh.dbSession, org, dbUser, false, true)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Get Expected Machine ID from URL param
expectedMachineID, err := uuid.Parse(c.Param("id"))
if err != nil {
Expand Down Expand Up @@ -677,6 +649,12 @@ func (uemh UpdateExpectedMachineHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Site details for Expected Machine", nil)
}

// Scope tenant privilege to the Expected Machine's Site.
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, uemh.dbSession, org, dbUser, false, &common.TenantPrivilegeScope{SiteID: &site.ID})
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Validate ProviderTenantSite relationship and site state
hasAccess, apiError := ValidateProviderOrTenantSiteAccess(ctx, logger, uemh.dbSession, site, infrastructureProvider, tenant)
if apiError != nil {
Expand Down Expand Up @@ -805,12 +783,6 @@ func (demh DeleteExpectedMachineHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve current user", nil)
}

// Ensure our user is a provider for the org
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, demh.dbSession, org, dbUser, false, true)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Get Expected Machine ID from URL param
expectedMachineID, err := uuid.Parse(c.Param("id"))
if err != nil {
Expand Down Expand Up @@ -838,6 +810,12 @@ func (demh DeleteExpectedMachineHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Site details for Expected Machine", nil)
}

// Scope tenant privilege to the Expected Machine's Site.
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, demh.dbSession, org, dbUser, false, &common.TenantPrivilegeScope{SiteID: &site.ID})
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Validate ProviderTenantSite relationship and site state
hasAccess, apiError := ValidateProviderOrTenantSiteAccess(ctx, logger, demh.dbSession, site, infrastructureProvider, tenant)
if apiError != nil {
Expand Down Expand Up @@ -929,12 +907,6 @@ func (cemh CreateExpectedMachinesHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve current user", nil)
}

// ensure our user is a provider or tenant for the org
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, cemh.dbSession, org, dbUser, false, true)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Validate request
// Bind request data to API model (array payload)
apiRequests := []model.APIExpectedMachineCreateRequest{}
Expand Down Expand Up @@ -1026,6 +998,12 @@ func (cemh CreateExpectedMachinesHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Site specified in request data due to DB error", nil)
}

// Scope tenant privilege to the common Site targeted by this batch.
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, cemh.dbSession, org, dbUser, false, &common.TenantPrivilegeScope{SiteID: &site.ID})
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Validate access to Site
hasAccess, apiError := ValidateProviderOrTenantSiteAccess(ctx, logger, cemh.dbSession, site, infrastructureProvider, tenant)
if apiError != nil {
Expand Down Expand Up @@ -1266,12 +1244,6 @@ func (uemh UpdateExpectedMachinesHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve current user", nil)
}

// Ensure our user is a provider or tenant for the org
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, uemh.dbSession, org, dbUser, false, true)
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Validate request
// Bind request data to API model (array payload)
apiRequests := []model.APIExpectedMachineUpdateRequest{}
Expand Down Expand Up @@ -1435,6 +1407,12 @@ func (uemh UpdateExpectedMachinesHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "No Site found for Expected Machines", nil)
}

// Scope tenant privilege to the common Site targeted by this batch.
infrastructureProvider, tenant, apiError := common.IsProviderOrTenant(ctx, logger, uemh.dbSession, org, dbUser, false, &common.TenantPrivilegeScope{SiteID: &site.ID})
if apiError != nil {
return cutil.NewAPIErrorResponse(c, apiError.Code, apiError.Message, apiError.Data)
}

// Validate ProviderTenantSite relationship and state
hasAccess, apiError := ValidateProviderOrTenantSiteAccess(ctx, logger, uemh.dbSession, site, infrastructureProvider, tenant)
if apiError != nil {
Expand Down
Loading
Loading