Private/Invoke-midPointRestMethod.ps1
|
function Invoke-midPointRestMethod { # The only place that talks to midPoint. Builds the URL and the Basic header from the # current session, sends JSON, and turns midPoint errors into terminating errors. # Returns an object with StatusCode, Headers and Body. [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet('GET', 'POST', 'PATCH', 'PUT', 'DELETE')] [string] $Method, # Path relative to <base>/ws/rest, e.g. 'users/<oid>' or 'users/search'. [Parameter(Mandatory)] [string] $Path, # Query parameters. An array value is sent as a repeated parameter # (midPoint expects ?options=raw&options=resolveNames). [System.Collections.IDictionary] $Query, # Request body; serialized to JSON. [object] $Body ) $session = Get-midPointSessionOrThrow $uri = '{0}/{1}' -f $session.Uri, $Path.TrimStart('/') if ($Query -and $Query.Count -gt 0) { $pairs = foreach ($key in $Query.Keys) { foreach ($value in @($Query[$key])) { if ($null -ne $value -and "$value" -ne '') { '{0}={1}' -f [uri]::EscapeDataString($key), [uri]::EscapeDataString([string]$value) } } } if ($pairs) { $uri = '{0}?{1}' -f $uri, ($pairs -join '&') } } $credential = $session.Credential $pair = '{0}:{1}' -f $credential.UserName, $credential.GetNetworkCredential().Password $headers = @{ Authorization = 'Basic ' + [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($pair)) Accept = 'application/json' } $httpParams = @{ Method = $Method Uri = $uri Headers = $headers SkipCertificateCheck = [bool]$session.SkipCertificateCheck } if ($session.TimeoutSec) { $httpParams.TimeoutSec = $session.TimeoutSec } if ($PSBoundParameters.ContainsKey('Body')) { $httpParams.Body = ConvertTo-Json -InputObject $Body -Depth 50 -Compress Write-Debug ("midPoint request body: " + ($httpParams.Body -replace '"clearValue":"(\\.|[^"\\])*"', '"clearValue":"***"')) } Write-Verbose "midPoint: $Method $uri" try { $response = Invoke-midPointHttp @httpParams } catch { $record = [System.Management.Automation.ErrorRecord]::new( [System.Net.Http.HttpRequestException]::new("midPoint request $Method $uri failed: $($_.Exception.Message)", $_.Exception), 'midPointConnectionFailed', [System.Management.Automation.ErrorCategory]::ConnectionError, $uri) throw $record } $status = [int]$response.StatusCode Write-Verbose "midPoint: HTTP $status" if ($status -ge 400) { $message = Get-midPointErrorMessage -Body $response.Body $category = switch ($status) { 400 { 'InvalidArgument' } 401 { 'AuthenticationError' } 403 { 'PermissionDenied' } 404 { 'ObjectNotFound' } 409 { 'ResourceExists' } default { 'InvalidOperation' } } $text = "midPoint returned HTTP $status for $Method $Path" if ($message) { $text = "$text`: $message" } elseif ($status -eq 401) { $text = "$text`: login rejected, check user name, password and that the user may use REST" } $exception = [System.Exception]::new($text) $exception.Data['StatusCode'] = $status $record = [System.Management.Automation.ErrorRecord]::new( $exception, "midPointHttp$status", [System.Management.Automation.ErrorCategory]$category, $uri) throw $record } # 240 = handled error, 250 = partial error (midPoint REST operation docs). if ($status -eq 250) { Write-Warning "midPoint reported a partial error for $Method $Path`: $(Get-midPointErrorMessage -Body $response.Body)" } elseif ($status -eq 240) { Write-Verbose "midPoint handled an error for $Method $Path`: $(Get-midPointErrorMessage -Body $response.Body)" } $response } function Get-midPointErrorMessage { # Pulls the most useful message out of a midPoint OperationResult (or any body). # Searches for the first non-empty 'message' property at any depth (works for 403 / 404 on 4.10, run 6). [CmdletBinding()] [OutputType([string])] param( [AllowNull()] [object] $Body ) if ($null -eq $Body) { return '' } if ($Body -is [string]) { return $Body.Trim() } $queue = [System.Collections.Generic.Queue[object]]::new() $queue.Enqueue($Body) while ($queue.Count -gt 0) { $item = $queue.Dequeue() if ($item -is [System.Management.Automation.PSCustomObject]) { $msg = $item.PSObject.Properties['message'] if ($msg -and $msg.Value -is [string] -and $msg.Value) { return $msg.Value } foreach ($p in $item.PSObject.Properties) { if ($null -ne $p.Value -and $p.Value -isnot [string]) { $queue.Enqueue($p.Value) } } } elseif ($item -is [System.Collections.IEnumerable]) { foreach ($child in $item) { if ($null -ne $child) { $queue.Enqueue($child) } } } } # No message anywhere: show the body itself (shortened) rather than nothing. $raw = ConvertTo-Json -InputObject $Body -Depth 10 -Compress if ($raw.Length -gt 500) { $raw = $raw.Substring(0, 500) + '...' } $raw } |