Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
function Select-CippAllowedTenantData {
<#
.SYNOPSIS
Narrow a set of cached rows to the tenants the current caller is allowed to see.

.DESCRIPTION
A family of cached List*/Exec*List endpoints reads a global cache table by PartitionKey
and returns the rows directly. When a tenant-restricted custom role calls one of these
with tenantFilter=AllTenants, the read must still be narrowed to the caller's allowed
tenants - the same scope Get-Tenants already applies via $script:CippAllowedTenantsStorage.
A direct cache-table read never touches Get-Tenants on a cache hit, so it would otherwise
leak every managed tenant's data. Pipe those rows through this function at the point they
become the response to close that gap.

This function MUST live in CIPPCore. The $script:CippAllowedTenantsStorage AsyncLocal slot
is CIPPCore module-scoped; a copy defined in CIPPHTTP would read that module's own empty
variable and silently filter nothing (see Get-CippRequestContext).

The stored scope is a list of customerIds (or $null = unrestricted). Cache rows identify
their tenant by domain name (defaultDomainName, stored on a 'Tenant' property) and/or by
customerId, so allowed customerIds are expanded to every identifier form an allowed tenant
might present, mirroring the match logic in Invoke-ListLogs.

.PARAMETER InputObject
The rows to filter. Accepts pipeline input.

.PARAMETER TenantProperty
The property name(s) on each row that identify its tenant. Defaults to 'Tenant' and
'TenantId'. A row is kept when any of these properties matches an allowed tenant. For the
Lighthouse aggregate use 'organizationId'.

.PARAMETER AllowPartner
Also keep rows whose Tenant equals 'CIPP' (system/partner rows), mirroring Invoke-ListLogs.

.EXAMPLE
$Rows = $Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant'

.FUNCTIONALITY
Internal
#>
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true)]
[AllowNull()]
$InputObject,

[string[]]$TenantProperty = @('Tenant', 'TenantId'),

[switch]$AllowPartner
)

begin {
# $null / empty stored scope means the caller is unrestricted - pass everything through
# with zero overhead (no Get-Tenants call).
$AllowedCustomerIds = if ($script:CippAllowedTenantsStorage) { $script:CippAllowedTenantsStorage.Value } else { $null }
$Unrestricted = -not ($AllowedCustomerIds | Where-Object { $_ })

if (-not $Unrestricted) {
# Build a case-insensitive set of every identifier a row might carry for an allowed
# tenant. Get-Tenants is already narrowed to the caller's scope by the storage filter.
$AllowedSet = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ($Id in $AllowedCustomerIds) {
if ($Id) { [void]$AllowedSet.Add([string]$Id) }
}
foreach ($Tenant in (Get-Tenants -IncludeErrors)) {
foreach ($Value in @($Tenant.customerId, $Tenant.defaultDomainName, $Tenant.initialDomainName)) {
if ($Value) { [void]$AllowedSet.Add([string]$Value) }
}
}
if ($AllowPartner) { [void]$AllowedSet.Add('CIPP') }
}
}

process {
foreach ($Item in $InputObject) {
if ($null -eq $Item) { continue }
if ($Unrestricted) {
$Item
continue
}
foreach ($Prop in $TenantProperty) {
$Value = $Item.$Prop
if ($Value -and $AllowedSet.Contains([string]$Value)) {
$Item
break
}
}
}
}
}
4 changes: 2 additions & 2 deletions Modules/CIPPCore/Public/Authentication/Test-CIPPAccess.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ function Test-CIPPAccess {
$swUserBranch = [System.Diagnostics.Stopwatch]::StartNew()
$User = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Request.Headers.'x-ms-client-principal')) | ConvertFrom-Json

if ($User.claims -and [string]::IsNullOrWhiteSpace($User.userDetails)) {
if ($User.claims -and [string]::IsNullOrWhiteSpace($User.userDetails)) {
$Claims = @($User.claims)
$Upn = ($Claims | Where-Object { $_.typ -in @('preferred_username', 'upn', 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn', 'email', 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress') } | Select-Object -First 1).val
if ([string]::IsNullOrWhiteSpace($Upn)) { $Upn = $Request.Headers.'x-ms-client-principal-name' }
Expand All @@ -159,7 +159,7 @@ function Test-CIPPAccess {

$swIPCheck = [System.Diagnostics.Stopwatch]::StartNew()
if (-not $User.userRoles) {
throw 'Access denied: unable to resolve roles for the authenticated principal.'
throw 'Access denied: unable to resolve roles for the authenticated principal'
}
$AllowedIPRanges = Get-CIPPRoleIPRanges -Roles $User.userRoles

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ function Start-AuditLogIngestionV2 {

# --- Download tenants: searches awaiting download (State = Created, due) ---
$DownloadTenants = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ($Row in @(Get-CIPPAzDataTableEntity @Ledger -Filter "State eq 'Created'" -Property @('PartitionKey', 'NextAttemptUtc'))) {
foreach ($Row in @(Get-CIPPAzDataTableEntity @Ledger -Filter "State eq 'Created'" -Property @('PartitionKey', 'RowKey', 'NextAttemptUtc'))) {
if ($Row.NextAttemptUtc -and ([datetimeoffset]$Row.NextAttemptUtc).UtcDateTime -gt $Now) { continue }
if ($Row.PartitionKey) { [void]$DownloadTenants.Add([string]$Row.PartitionKey) }
}

# --- Process-only tenants: rows pending in the webhook cache (downloaded, not yet processed) ---
$CacheTable = Get-CippTable -TableName 'CacheWebhooks'
$CacheTenants = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ($Row in @(Get-CIPPAzDataTableEntity @CacheTable -Property @('PartitionKey'))) {
foreach ($Row in @(Get-CIPPAzDataTableEntity @CacheTable -Property @('PartitionKey', 'RowKey'))) {
if ($Row.PartitionKey) { [void]$CacheTenants.Add([string]$Row.PartitionKey) }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,18 @@ function Start-SchedulerOrchestrator {
$Table = Get-CIPPTable -TableName SchedulerConfig
$Tenants = Get-CIPPAzDataTableEntity @Table | Where-Object -Property PartitionKey -NE 'WebhookAlert'

$ValidTypes = @('CIPPNotifications', 'webhookcreation')

$Tasks = foreach ($Tenant in $Tenants) {
if ($Tenant.type -notin $ValidTypes) {
if ($Tenant.PartitionKey -eq 'Alert') {
Write-Information "Scheduler: removing legacy classic-alert row for '$($Tenant.tenant)'"
Remove-AzDataTableEntity -Force @Table -Entity $Tenant
} else {
Write-Information "Scheduler: skipping row $($Tenant.PartitionKey)/$($Tenant.RowKey) - no handler for type '$($Tenant.type)'"
}
continue
}
if ($Tenant.tenant -ne 'AllTenants') {
[pscustomobject]@{
Tenant = $Tenant.tenant
Expand Down
1 change: 1 addition & 0 deletions Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

function Get-CIPPAuthentication {
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Wraps a resolved tenant GUID in a SecureString only to satisfy Set-CippKeyVaultSecret; the value is written to Azure Key Vault (encrypted at rest)')]
param (
$APIName = 'Get Keyvault Authentication',
[switch]$Force
Expand Down
1 change: 1 addition & 0 deletions Modules/CIPPCore/Public/Get-CippKeyVaultSecret.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function Get-CippKeyVaultSecret {
Get-CippKeyVaultSecret -VaultName 'mykeyvault' -Name 'RefreshToken' -AsPlainText
#>
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Wraps the value returned by the Key Vault REST call in a SecureString to match the Get-AzKeyVaultSecret return shape')]
param(
[Parameter(Mandatory = $false)]
[string]$VaultName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function Get-AuthorisedRequest {
$TenantID = $env:TenantID
}

if ($Uri -like 'https://graph.microsoft.com/beta/contracts*' -or $Uri -like '*/customers/*' -or $Uri -eq 'https://graph.microsoft.com/v1.0/me/sendMail' -or $Uri -like '*/tenantRelationships/*' -or $Uri -like '*/security/partner/*' -or $Uri -like '*/organization') {
if ($Uri -like 'https://graph.microsoft.com/beta/contracts*' -or $Uri -like '*/customers/*' -or $Uri -eq 'https://graph.microsoft.com/v1.0/me/sendMail' -or $Uri -like '*/tenantRelationships/*' -or $Uri -like '*/security/partner/*' -or $Uri -match '/organization(\?|$)') {
return $true
}
$Tenant = Get-Tenants -IncludeErrors -TenantFilter $TenantID | Where-Object { $_.Excluded -eq $false }
Expand Down
2 changes: 2 additions & 0 deletions Modules/CIPPCore/Public/Set-CIPPNotificationConfig.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
function Set-CIPPNotificationConfig {
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Wraps a webhook auth config value in a SecureString only to satisfy Set-CippKeyVaultSecret; the value is written to Azure Key Vault (encrypted at rest)')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUsernameAndPasswordParams', '', Justification = 'webhookAuthUsername/webhookAuthPassword are stored config values for an outbound webhook integration, not interactive credentials')]
param (
$email,
$webhook,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ function Invoke-ExecBackupReplicationConfig {
CIPP.AppSettings.ReadWrite
#>
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Wraps a backup SAS URL in a SecureString only to satisfy Set-CippKeyVaultSecret; the value is written to Azure Key Vault (encrypted at rest)')]
param($Request, $TriggerMetadata)

$Table = Get-CIPPTable -TableName Config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function Invoke-ListMailboxRules {
$Metadata = [PSCustomObject]@{
QueueId = $RunningQueue.RowKey ?? $null
}
$GraphRequest = $Rows | ForEach-Object {
$GraphRequest = $Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant' | ForEach-Object {
$NewObj = $_.Rules | ConvertFrom-Json -ErrorAction SilentlyContinue
$NewObj | Add-Member -NotePropertyName 'Tenant' -NotePropertyValue $_.Tenant -Force
$NewObj
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function Invoke-ListMailQuarantine {
$Metadata = [PSCustomObject]@{
QueueId = $RunningQueue.RowKey ?? $null
}
$Messages = $Rows
$Messages = $Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant'
foreach ($message in $Messages) {
$messageObj = $message.QuarantineMessage | ConvertFrom-Json
$messageObj | Add-Member -NotePropertyName 'Tenant' -NotePropertyValue $message.Tenant -Force
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function Invoke-ListTransportRules {
$Metadata = [PSCustomObject]@{
QueueId = $RunningQueue.RowKey ?? $null
}
$Rules = $Rows
$Rules = $Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant'
foreach ($rule in $Rules) {
$RuleObj = $rule.TransportRule | ConvertFrom-Json
$RuleObj | Add-Member -MemberType NoteProperty -Name Tenant -Value $rule.Tenant -Force
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
}
# There is data in the cache, so we will use that
Write-Information "Found $($Rows.Count) rows in the cache"
foreach ($row in $Rows) {
foreach ($row in ($Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant')) {
$UserObject = $row.JITAdminUser | ConvertFrom-Json
$Results.Add(
[PSCustomObject]@{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ Function Invoke-ListUsers {
$_ | Add-Member -MemberType NoteProperty -Name 'primDomain' -Value @{value = ($_.userPrincipalName -split '@' | Select-Object -Last 1); label = ($_.userPrincipalName -split '@' | Select-Object -Last 1); } -Force
$_
}
} elseif ((Get-CippRequestContext).AllowedTenants) {
# Deprecated cacheusers blob has no reliable per-tenant column, so it cannot be safely
# narrowed for a tenant-restricted caller. Return the deprecation message instead of
# leaking every tenant's users. Unrestricted callers keep the legacy behavior below.
[PSCustomObject]@{
Message = 'This function has been deprecated for all users, please use ListGraphRequest instead'
}
} else {
$Table = Get-CIPPTable -TableName 'cacheusers'
$Rows = Get-CIPPAzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddHours(-1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Function Invoke-ListBasicAuth {
Body = @($GraphRequest)
})
} else {
$GraphRequest = $Rows
$GraphRequest = @($Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant')
return ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
Body = @($GraphRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ function Invoke-ListMFAUsers {
} else {
Write-Information 'Getting cached MFA state for all tenants'
Write-Information "Found $($Rows.Count) rows in cache"
$Rows = $Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant'
$Rows = foreach ($Row in $Rows) {
if ($Row.CAPolicies -and $Row.CAPolicies -is [string]) {
$Row.CAPolicies = try { $Row.CAPolicies | ConvertFrom-Json -ErrorAction Stop } catch { @() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ Function Invoke-ListAllTenantDeviceCompliance {
$TenantFilter = $Request.Query.TenantFilter
try {
if ($TenantFilter -eq 'AllTenants') {
$GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedDeviceCompliances'
# The Lighthouse aggregate returns every managed tenant with no per-caller scoping, so
# narrow it to the tenants this caller is allowed to see (organizationId = customerId).
$GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedDeviceCompliances' | Select-CippAllowedTenantData -TenantProperty 'organizationId'
$StatusCode = [HttpStatusCode]::OK
} else {
$GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/managedTenants/managedDeviceCompliances?`$top=999&`$filter=organizationId eq '$TenantFilter'"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function Invoke-ListLicenses {
Write-Host "Started permissions orchestration with ID = '$InstanceId'"
}
} else {
$GraphRequest = $Rows | ForEach-Object {
$GraphRequest = $Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant' | ForEach-Object {
$LicenseData = $_.License | ConvertFrom-Json -ErrorAction SilentlyContinue
foreach ($License in $LicenseData) {
$License
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function Invoke-ExecAlertsList {
QueueId = $RunningQueue.RowKey ?? $null
}

$Alerts = $Rows
$Alerts = $Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant'
$AlertsObj = foreach ($Alert in $Alerts) {
$AlertInfo = $Alert.Alert | ConvertFrom-Json
@{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function Invoke-ExecIncidentsList {
$Metadata = [PSCustomObject]@{
QueueId = $RunningQueue.RowKey ?? $null
}
$Incidents = $Rows
$Incidents = $Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant'
foreach ($incident in $Incidents) {
if ($incident.Incident -and (Test-Json -Json $incident.Incident)) {
$IncidentObj = $incident.Incident | ConvertFrom-Json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function Invoke-ExecMDOAlertsList {
$Metadata = [PSCustomObject]@{
QueueId = $RunningQueue.RowKey ?? $null
}
$Alerts = $Rows
$Alerts = $Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant'
foreach ($alert in $Alerts) {
ConvertFrom-Json -InputObject $alert.MdoAlert -Depth 10
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ function Invoke-ListConditionalAccessPolicies {
$Metadata = [PSCustomObject]@{
QueueId = $RunningQueue.RowKey ?? $null
}
$Policies = $Rows
# Output all policies from all tenants
$Policies = $Rows | Select-CippAllowedTenantData -TenantProperty 'Tenant'
# Output all policies from all tenants the caller is allowed to see
foreach ($policy in $Policies) {
($policy.Policy | ConvertFrom-Json)
}
Expand Down
Loading