Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.ps1
|
Function ConvertTo-ACETokenList { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$SecurityNamespace, [Parameter(Mandatory = $true)] [Object[]]$ACEPermissions ) Write-Verbose "[ConvertTo-ACETokenList] Initializing the ACL Token." $hashTableArray = [System.Collections.Generic.List[HashTable]]::new() Write-Verbose "[ConvertTo-ACETokenList] Performing a Lookup for the Security Descriptor." Write-Verbose "[ConvertTo-ACETokenList] Security Namespace: $SecurityNamespace" $SecurityDescriptor = Get-CacheItem -Key $SecurityNamespace -Type 'SecurityNamespaces' # Check if the Security Descriptor was found if (-not $SecurityDescriptor) { Write-Error "Security Descriptor not found for namespace: $SecurityNamespace" return } # Some namespaces expose actions that are documented in the namespace's action list but which the # API refuses to let a caller explicitly Allow/Deny - it rejects the ENTIRE batched ACL update with # a 500 "VS403284: ... reserved by the system" error (confirmed live; matches reports against the # official Terraform azuredevops provider for the same bit). Since Set-AzDoPermission's caller never # sees that failure (non-terminating Write-Error), a config that requests one of these bits silently # drops every ACE in the same batch, not just its own. Strip them here rather than let the whole # write fail - they are implicitly available regardless (that is exactly why the platform reserves # them), so omitting an explicit grant does not change effective access. $reservedActionsByNamespace = @{ 'Process' = @('ReadProcessPermissions') } $reservedActions = $reservedActionsByNamespace[$SecurityNamespace] # Iterate through each of the ACEs and construct the ACE Object Write-Verbose "[ConvertTo-ACETokenList] Iterating through each of the ACE Permissions." ForEach ($ACEPermission in $ACEPermissions) { # Check to see if there are any permissions that are not found in the Security Descriptor $missingPermissions = $ACEPermission.Keys | Where-Object { ($_ -notin $SecurityDescriptor.actions.displayName) -and ($_ -notin $SecurityDescriptor.actions.name) } | ForEach-Object { Write-Verbose "[ConvertTo-ACETokenList] Permission '$_' not found in the Security Descriptor for namespace: $SecurityNamespace" } # Filter the Allow and Deny permissions Write-Verbose "[ConvertTo-ACETokenList] ACEPermission: $($ACEPermission | ConvertTo-Json)" Write-Verbose "[ConvertTo-ACETokenList] Filtering Allow and Deny permissions." $AllowPermissions = $ACEPermission.Keys | Where-Object { $ACEPermission."$_" -eq 'Allow' } $DenyPermissions = $ACEPermission.Keys | Where-Object { $ACEPermission."$_" -eq 'Deny' } if ($reservedActions) { foreach ($reserved in $reservedActions) { if ($reserved -in $AllowPermissions -or $reserved -in $DenyPermissions) { Write-Warning "[ConvertTo-ACETokenList] '$reserved' is reserved by the '$SecurityNamespace' namespace and cannot be explicitly Allow/Deny'd via the API - omitting it (it is implicitly available regardless)." } } $AllowPermissions = @($AllowPermissions | Where-Object { $_ -notin $reservedActions }) $DenyPermissions = @($DenyPermissions | Where-Object { $_ -notin $reservedActions }) } Write-Verbose "[ConvertTo-ACETokenList] Iterating through the Allow and Deny Permissions and computing actions." $AllowBits = $SecurityDescriptor.actions | Where-Object { ($_.displayName -in $AllowPermissions) -or ($_.name -in $AllowPermissions) } $DenyBits = $SecurityDescriptor.actions | Where-Object { ($_.displayName -in $DenyPermissions) -or ($_.name -in $DenyPermissions) } # Compute the bitwise OR for the permissions $hashTable = @{ DescriptorType = $SecurityNamespace Allow = $AllowBits Deny = $DenyBits } Write-Verbose "[ConvertTo-ACETokenList] Adding computed hash table to the array" Write-Verbose "[ConvertTo-ACETokenList] Hash Table: $($hashTable | ConvertTo-Json)" $hashTableArray.Add($hashTable) } Write-Verbose "[ConvertTo-ACETokenList] Completed processing ACE Permissions" # Return the hashtable array return $hashTableArray } |