CatalystDosIdentity.psm1
<#
.Synopsis Attempts to get an access token for given client .Description Takes in a client id and secret with valid scope and hits the identity server provided and returns an access token. .Parameter identityUrl The identity url to get the access token from .Parameter clientId The id of the client the access token is being requested for .Parameter secret The secret of the client the access token is being requested for .Parameter scopes The scope of the access token to be requested .Example Get-AccessToken -identityUrl "https://server/identity" -clientId "sample-client-id" -secret "SECrEtStrING" -scopes "offline_access" #> function Get-AccessToken { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] [string] $clientId, [Parameter(Mandatory = $True)] [string] $secret, [string] $scope ) [Uri] $url = "$($identityUrl.OriginalString.TrimEnd("/"))/connect/token" $body = @{ client_id = $clientId grant_type = "client_credentials" scope = $scope client_secret = $secret } $accessTokenResponse = Invoke-RestMethod -Method Post -Uri $url -Body $body return $accessTokenResponse.access_token } <# .Synopsis Attempts to get an access token for the fabric installer client .Description Takes in the identity server url and the installer secret and return the installer access token. .Parameter identityUrl The identity url to get the access token from .Parameter secret The secret of the fabric installer client .Example Get-AccessToken -identityUrl "https://server/identity" -secret "SECrEtStrING" #> function Get-FabricInstallerAccessToken { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] [string] $secret ) $clientId = "fabric-installer" $scope = "fabric/identity.manageresources fabric/authorization.read fabric/authorization.write fabric/authorization.dos.write fabric/authorization.manageclients" return Get-AccessToken $identityUrl $clientId $secret $scope } <# .Synopsis Attempts to retrieve information about an existing identity client .Description Takes in the identity server url, the clientid and an access token. Returns a client identity object .Parameter identityUrl The base identity url .Parameter clientId the identifier for the client to retrieve .Parameter accessToken an access token previously retrieved from the identity server .Example Get-ClientRegistration -identityUrl "https://server/identity" -clientId "sample-client-id" -accessToken "eyJhbGciO" #> function Get-ClientRegistration { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] [string] $clientId, [Parameter(Mandatory = $True)] [string] $accessToken ) [Uri]$url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/v1/client/$clientId" $headers = @{"Accept" = "application/json"} $headers.Add("Authorization", "Bearer $accessToken") $clientResponse = Invoke-RestMethod -Method Get -Uri $url -Headers $headers return $clientResponse } <# .Synopsis Attempts to register a new identity client .Description Takes in the identity server url, the json client body and an access token. Returns a client secret for the new client. If the client already exists, this function updates the client attributes and resets the client secret. .Parameter identityUrl The base identity url .Parameter body the json payload of attributes and values for the new client .Parameter accessToken an access token previously retrieved from the identity server .Example New-ClientRegistration -identityUrl "https://server/identity" -body '{"clientId":"fabric-installer", "clientName":"Fabric Installer" ...... }' -accessToken "eyJhbGciO" #> function New-ClientRegistration { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] $body, [Parameter(Mandatory = $True)] [string] $accessToken ) [Uri] $url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/client" if (!($body -is [string])) { $clientObject = $body $body = $body | ConvertTo-Json } else { $clientObject = ConvertFrom-Json -InputObject $body } $headers = @{"Accept" = "application/json"} if ($accessToken) { $headers.Add("Authorization", "Bearer $accessToken") } # attempt to add try { $registrationResponse = Invoke-RestMethod -Method Post -Uri $url -Body $body -ContentType "application/json" -Headers $headers return $registrationResponse.clientSecret } catch { $exception = $_.Exception if ((Assert-WebExceptionType -exception $exception -typeCode 409)) { try { # client ID already exists, update with PUT Invoke-RestMethod -Method Put -Uri "$url/$($clientObject.clientId)" -Body $body -ContentType "application/json" -Headers $headers | out-null # Reset client secret $apiResponse = Invoke-RestMethod -Method Post -Uri "$url/$($clientObject.clientId)/resetPassword" -ContentType "application/json" -Headers $headers return $apiResponse.clientSecret } catch { $error = "Unknown error attempting to update" $exception = $_.Exception if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw (New-Object -TypeName "System.Net.WebException" "There was an error updating Client $($clientObject.clientName): $error. Halting installation.", $exception) } } else { $error = "Unknown error attempting to post" $exception = $_.Exception if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw ( New-Object -TypeName "System.Net.WebException" "There was an error registering client $($clientObject.clientName) with Fabric.Identity: $error, halting installation.", $exception) } } } <# .Synopsis Creates a Client Credentials Identity Client .Description Returns a new hashtable that represents a valid identity client body for use with New-ClientRegistration. Sensible defaults values are included. .Parameter clientId The text identiifier for the client .Parameter clientName The human readable text name for the client .Parameter allowedScopes Array of strings representing the allowed scopes for the client .Example New-ClientCredentialsClientBody -clientId "sample-credential-client" -clientName "Sample Client using Client Credentials" -allowedScopes @("dos/metadata.read") #> function New-ClientCredentialsClientBody { param( [Parameter(Mandatory = $True)] [string] $clientId, [Parameter(Mandatory = $True)] [string] $clientName, [Parameter(Mandatory = $True)] [AllowEmptyCollection()] [string[]] $allowedScopes ) # parameters $newClient = @{} $newClient.Add("clientId", $clientId) $newClient.Add("clientName", $clientName) $newClient.Add("allowedScopes", $allowedScopes) $newClient.Add("allowedGrantTypes", @("client_credentials")) return $newClient } <# .Synopsis Creates an Implicit Identity Client .Description Retruns a new hashtable that represents a valid identity client body for use with New-ClientRegistration. Sensible defaults values are included. .Parameter clientId The text identiifier for the client .Parameter clientName The human readable text name for the client .Parameter allowedScopes Array of strings representing the allowed scopes for the client .Parameter allowedCorsOrigins Array of strings representing the allowed addresses for Cross-Origin Resource Sharing .Parameter redirectUris Array of strings representing list of uri's that can request a login redirect .Parameter postLogoutRedirectUris Array of strings representing list of uri's that are acceptable navigation after logout .Example New-ImplicitClientBody -clientId "sample-implicit-client" -clientName "Sample Client using Implicit Registration" -allowesScopes @("dos/metadata.read") -allowesCorsOrigins @('http://some.server', 'https://some.other.server') -redirectUris @('https://some.server/loginRedirect') -postLogoutRedirectUris @('https://some.other.server/logoutRedirect') #> function New-ImplicitClientBody { param( [Parameter(Mandatory = $True)] [string] $clientId, [Parameter(Mandatory = $True)] [string] $clientName, [Parameter(Mandatory = $True)] [AllowEmptyCollection()] [string[]] $allowedScopes, [Parameter(Mandatory = $false)] [string[]] $allowedCorsOrigins, [Parameter(Mandatory = $false)] [string[]] $redirectUris, [Parameter(Mandatory = $false)] [string[]] $postLogoutRedirectUris ) # parameters $newClient = @{} $newClient.Add("clientId", $clientId) $newClient.Add("clientName", $clientName) $newClient.Add("allowedScopes", $allowedScopes) $newClient.Add("allowedCorsOrigins", $allowedCorsOrigins) $newClient.Add("redirectUris", $redirectUris ) $newClient.Add("postLogoutRedirectUris", $postLogoutRedirectUris) $newClient.Add("allowedGrantTypes", @("implicit")) $newClient.Add("requireConsent", $false) $newClient.Add("allowOfflineAccess", $false) $newClient.Add("allowAccessTokensViaBrowser", $true) $newClient.Add("enableLocalLogin", $false) $newClient.Add("accessTokenLifetime", 1200) return $newClient } <# .Synopsis Creates an Hybryd Identity Client .Description Retruns a new hashtable that represents a valid identity client body for use with New-ClientRegistration. Sensible defaults values are included. .Parameter clientId The text identiifier for the client .Parameter clientName The human readable text name for the client .Parameter allowedScopes Array of strings representing the allowed scopes for the client .Parameter allowedCorsOrigins Array of strings representing the allowed addresses for Cross-Origin Resource Sharing .Parameter redirectUris Array of strings representing list of uri's that can request a login redirect .Parameter postLogoutRedirectUris Array of strings representing list of uri's that are acceptable navigation after logout .Example New-HybridClientBody -clientId "sample-hybrid-client" -clientName "Sample Client using Hybrid Registration" -allowesScopes @("dos/metadata.read") -allowesCorsOrigins @('http://some.server', 'https://some.other.server') -redirectUris @('https://some.server/loginRedirect') -postLogoutRedirectUris @('https://some.other.server/logoutRedirect') #> function New-HybridClientBody { param( [Parameter(Mandatory = $True)] [string] $clientId, [Parameter(Mandatory = $True)] [string] $clientName, [Parameter(Mandatory = $True)] [AllowEmptyCollection()] [string[]] $allowedScopes, [Parameter(Mandatory = $false)] [string[]] $allowedCorsOrigins, [Parameter(Mandatory = $false)] [string[]] $redirectUris, [Parameter(Mandatory = $false)] [string[]] $postLogoutRedirectUris ) # parameters $newClient = @{} $newClient.Add("clientId", $clientId) $newClient.Add("clientName", $clientName) $newClient.Add("allowedScopes", $allowedScopes) $newClient.Add("allowedCorsOrigins", $allowedCorsOrigins) $newClient.Add("redirectUris", $redirectUris ) $newClient.Add("postLogoutRedirectUris", $postLogoutRedirectUris) $newClient.Add("allowedGrantTypes", @("hybrid")) $newClient.Add("requireConsent", $false) $newClient.Add("allowOfflineAccess", $true) $newClient.Add("allowAccessTokensViaBrowser", $false) $newClient.Add("enableLocalLogin", $false) return $newClient } <# .Synopsis Creates an Hybryd and PKCE Identity Client .Description Retruns a new hashtable that represents a valid identity client body for use with New-ClientRegistration. Sensible defaults values are included. .Parameter clientId The text identiifier for the client .Parameter clientName The human readable text name for the client .Parameter allowedScopes Array of strings representing the allowed scopes for the client .Parameter redirectUris Array of strings representing list of uri's that can request a login redirect .Example New-HybridPkceClientBody -clientId "sample-hybridpkce-client" -clientName "Sample Client using Hybrid and PKCE Registration" -allowesScopes @("dos/metadata.read") -redirectUris @('https://some.server/loginRedirect') #> function New-HybridPkceClientBody { param( [Parameter(Mandatory = $True)] [string] $clientId, [Parameter(Mandatory = $True)] [string] $clientName, [Parameter(Mandatory = $True)] [AllowEmptyCollection()] [string[]] $allowedScopes, [Parameter(Mandatory = $false)] [string[]] $redirectUris ) # parameters $newClient = @{} $newClient.Add("clientId", $clientId) $newClient.Add("clientName", $clientName) $newClient.Add("allowedScopes", $allowedScopes) $newClient.Add("redirectUris", $redirectUris ) $newClient.Add("allowedGrantTypes", @("hybrid")) $newClient.Add("requireConsent", $false) $newClient.Add("requireClientSecret", $false) $newClient.Add("allowOfflineAccess", $true) $newClient.Add("requirePkce", $true) $newClient.Add("updateAccessTokenClaimsOnRefresh", $true) return $newClient } <# .Synopsis Attempts to update an identity client .Description Takes in the identity server url, the json client body and an access token. .Parameter identityUrl The identity url to get the access token from .Parameter body the json payload of attributes and values for the new client .Parameter accessToken an access token previously retrieved from the identity server .Example Edit-ClientRegistration -identityUrl "https://server/identity" -body @'{"clientId":"fabric-installer", "clientName":"Fabric Installer" ...... }@' -accessToken "eyJhbGciO" #> function Edit-ClientRegistration { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] $body, [Parameter(Mandatory = $True)] [string] $accessToken ) if (!($body -is [string])) { $clientObject = $body $body = $body | ConvertTo-Json } else { $clientObject = ConvertFrom-Json -InputObject $body } [Uri] $url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/client" $headers = @{"Accept" = "application/json"} if ($accessToken) { $headers.Add("Authorization", "Bearer $accessToken") } # attempt to PUT try { $updateReturn = Invoke-RestMethod -Method Put -Uri "$url/$($clientObject.clientId)" -Body $body -ContentType "application/json" -Headers $headers return $updateReturn } catch { $error = "Unknown error." $exception = $_.Exception if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw ( New-Object -TypeName "System.Net.WebException" "There was an error updating client registration $($clientObject.clientName) with Fabric.Identity: $error, halting installation.", $exception) } } <# .Synopsis Attempts to update the client secret of an identity client .Description Takes in the identity server url, the json client ID and an access token. .Parameter identityUrl The identity url to get the access token from .Parameter clientId the unique identifier for the client to reset .Parameter accessToken an access token previously retrieved from the identity server .Example Reset-ClientPassword -identityUrl "https://server/identity" -clientId "someClient" -accessToken "eyJhbGciO" #> function Reset-ClientPassword { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] [string] $clientId, [Parameter(Mandatory = $True)] [string] $accessToken ) [Uri] $url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/client" $headers = @{"Accept" = "application/json"} if ($accessToken) { $headers.Add("Authorization", "Bearer $accessToken") } # attempt to Reset try { $apiResponse = Invoke-RestMethod -Method Post -Uri "$url/$clientId/resetPassword" -ContentType "application/json" -Headers $headers return $apiResponse.clientSecret } catch { $error = "Unknown error." $exception = $_.Exception if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw ( New-Object -TypeName "System.Net.WebException" "There was an error resetting client secret $clientId with Fabric.Identity: $error, halting installation.", $exception) } } <# .Synopsis checks for the existence of a specific identity client .Description Takes in the identity server url, the json client ID and an access token. .Parameter identityUrl The identity url to get the access token from .Parameter clientId the unique identifier for the client to check .Parameter accessToken an access token previously retrieved from the identity server .Example Test-IsClientRegistered -identityUrl "https://server/identity" -clientId "someClient" -accessToken "eyJhbGciO" #> function Test-IsClientRegistered { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] [string] $clientId, [Parameter(Mandatory = $True)] [string] $accessToken ) [Uri] $url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/client/$clientId" $headers = @{"Accept" = "application/json"} $headers.Add("Authorization", "Bearer $accessToken") try { Invoke-RestMethod -Method Get -Uri $url -Headers $headers | Out-Null # exception thrown if not found return $True } catch { $exception = $_.Exception if (Assert-WebExceptionType -exception $exception -typeCode 404) { try { return $false } catch { $error = "Unknown error." $exception = $_.Exception if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw ( New-Object -TypeName "System.Net.WebException" "There was an error looking for client $clientId with Fabric.Identity: $error, halting installation.", $exception) } } else { $error = "Unknown error." $exception = $_.Exception if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw ( New-Object -TypeName "System.Net.WebException" "There was an error looking for client $clientId with Fabric.Identity: $error, halting installation.", $exception) } } } <# .Synopsis Attempts to retrieve information about an existing identity api .Description Takes in the identity server url the apiname and an access token. .Parameter identityUrl The identity url to get the api registration .Parameter apiName The name of the api .Parameter accessToken An access token previously retrieved from the identity server .Example Get-ApiRegistration -identityUrl "https://server/identity" -apiName "TestAPI" -accessToken "eyJhbGciO" #> function Get-ApiRegistration { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] [string] $apiName, [Parameter(Mandatory = $True)] [string] $accessToken ) [Uri]$url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/apiresource/$apiName" $headers = @{"Accept" = "application/json"} if ($accessToken) { $headers.Add("Authorization", "Bearer $accessToken") } $clientResponse = Invoke-RestMethod -Method Get -Uri $url -Headers $headers return $clientResponse } <# .Synopsis Attempts to register an api with fabric identity .Description Takes in the identity server url the json request body and an access token. .Parameter identityUrl The identity url to post the api registration .Parameter body The json request format for creating the api registration .Parameter accessToken An access token previously retrieved from the identity server .Example New-ApiRegistration -identityUrl "https://server/identity" -body "{"enabled":true, "name": "sample-api", "userClaims":[], "scopes":[]}" -accessToken "eyJhbGciO" #> function New-ApiRegistration { param( [Parameter(Mandatory=$True)] [Uri] $identityUrl, [Parameter(Mandatory=$True)] $body, [Parameter(Mandatory=$True)] [string] $accessToken ) $url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/apiresource" $headers = @{"Accept" = "application/json"} if ($accessToken) { $headers.Add("Authorization", "Bearer $accessToken") } try{ if(!($body -is [string])) { $body = $body | ConvertTo-Json } $registrationResponse = Invoke-RestMethod -Method Post -Uri $url -body $body -ContentType "application/json" -Headers $headers return $registrationResponse.apiSecret } catch { $exception = $_.Exception $apiResourceObject = ConvertFrom-Json -InputObject $body if ((Assert-WebExceptionType -exception $exception -typeCode 409)) { try { Invoke-RestMethod -Method Put -Uri "$url/$($apiResourceObject.name)" -Body $body -ContentType "application/json" -Headers $headers | out-null # Reset api secret $apiResponse = Reset-ApiPassword -identityUrl $($identityUrl.OriginalString) -apiName $($apiResourceObject.name) -accessToken $accessToken return $apiResponse }catch{ $error = "Unknown error attempting to post api" $exception = $_.Exception if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw (New-Object -TypeName "System.Net.WebException" "There was an error registering api $($apiResourceObject.name): $error. Registration failure.", $exception) } } else { $error = "Unknown error attempting to post" $exception = $_.Exception if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw ( New-Object -TypeName "System.Net.WebException" "There was an error registering api $($apiResourceObject.name) with Fabric.Identity: $error, Registration failure.", $exception) } } } <# .Synopsis Attempts to create an apiresource object .Description Takes in the apiname userclaims scopes and isenabled. .Parameter apiName The name of the api .Parameter userClaims Array of strings representing the user claims .Parameter scopes Array of Hashtables representing the scopes for the api .Parameter isEnabled If the apiresource is enabled .Example New-ApiRegistrationBody -apiName "this-Api" -userClaims @("name", "email", "role", "groups") -scopes @{"name" = "this-Api"; "displayName" = "This-API"} -isEnabled true #> function New-ApiRegistrationBody { param( [Parameter(Mandatory = $True)] [string] $apiName, [string[]] $userClaims, [Hashtable[]] $scopes, [string] $isEnabled ) $newApiResource = @{} $newApiResource.Add("name", $apiName) $newApiResource.Add("scopes", $scopes) $newApiResource.Add("userClaims", $userClaims) $newApiResource.Add("enabled", $isEnabled) return $newApiResource } <# .Synopsis Attempts to remove the api object .Description Takes in the identity server url an apiname and an access token. .Parameter identityUrl The identity url to delete the api registration .Parameter apiName The name of the api .Parameter accessToken An access token previously retrieved from the identity server .Example Remove-ApiRegistration -identityUrl "https://server/identity" -apiName "TestAPI" -accessToken "eyJhbGciO" #> function Remove-ApiRegistration { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] [string] $apiName, [Parameter(Mandatory = $True)] [string] $accessToken ) [Uri]$url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/apiresource/$apiName" $headers = @{"Accept" = "application/json"} if ($accessToken) { $headers.Add("Authorization", "Bearer $accessToken") } try { $clientResponse = Invoke-RestMethod -Method Delete -Uri $url -Headers $headers return $clientResponse } catch { $exception = $_.Exception $error = "Unknown error attempting to delete api" if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw ( New-Object -TypeName "System.Net.WebException" "There was an error deleting api $apiName with Fabric.Identity: $error, Removing api registration failure.", $exception) } } <# .Synopsis Attempts to edit an api object with fabric identity .Description Takes in the identity server url the json request body an apiname and an access token. .Parameter identityUrl The identity url to put the api registration .Parameter body The json request format for editing the api registration .Parameter apiName The name of the api .Parameter accessToken An access token previously retrieved from the identity server .Example Edit-ApiRegistration -identityUrl "https://server/identity" -body "{"enabled":true, "name": "sample-api", "userClaims":[], "scopes":[]}" -apiName "TestAPI" -accessToken "eyJhbGciO" #> function Edit-ApiRegistration { param( [Parameter(Mandatory=$True)] [Uri] $identityUrl, [Parameter(Mandatory=$True)] $body, [Parameter(Mandatory=$True)] [string] $apiName, [Parameter(Mandatory=$True)] [string] $accessToken ) $url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/apiresource/$apiName" $headers = @{"Accept" = "application/json"} if ($accessToken) { $headers.Add("Authorization", "Bearer $accessToken") } try{ if(!($body -is [string])) { $body = $body | ConvertTo-Json } Invoke-RestMethod -Method Put -uri $url -body $body -ContentType "application/json" -Headers $headers | out-null # Reset api secret $apiResponse = Reset-ApiPassword -identityUrl $($identityUrl.OriginalString) -apiName $apiName -accessToken $accessToken return $apiResponse }catch{ $exception = $_.Exception $apiResourceObject = ConvertFrom-Json -InputObject $body $error = "Unknown error attempting to edit api" if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw ( New-Object -TypeName "System.Net.WebException" "There was an error editing api $($apiResourceObject.name) with Fabric.Identity: $error, Editing registration failure.", $exception) } } <# .Synopsis Attempts to reset an api object password with fabric identity .Description Takes in the identity server url an apiname and an access token. .Parameter identityUrl The identity url to reset the api password .Parameter apiName The name of the api .Parameter accessToken An access token previously retrieved from the identity server .Example Reset-ApiPassword -identityUrl "https://server/identity" -apiName "TestAPI" -accessToken "eyJhbGciO" #> function Reset-ApiPassword { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] [string] $apiName, [Parameter(Mandatory = $True)] [string] $accessToken ) [Uri]$url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/apiresource/$apiName/resetPassword" $headers = @{"Accept" = "application/json"} if ($accessToken) { $headers.Add("Authorization", "Bearer $accessToken") } try { # Reset api secret $apiResponse = Invoke-RestMethod -Method Post -Uri $url -ContentType "application/json" -Headers $headers return $apiResponse.apiSecret } catch { $exception = $_.Exception $error = "Unknown error attempting to reset api" if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw ( New-Object -TypeName "System.Net.WebException" "There was an error resetting api password $apiName with Fabric.Identity: $error, Resetting api password failure.", $exception) } } <# .Synopsis Attempts to get an api object registration .Description Takes in the identity server url an apiname and an access token. .Parameter identityUrl The identity url to get the api object .Parameter apiName The name of the api .Parameter accessToken An access token previously retrieved from the identity server .Example Test-IsApiRegistered -identityUrl "https://server/identity" -apiName "TestAPI" -accessToken "eyJhbGciO" #> function Test-IsApiRegistered { param( [Parameter(Mandatory = $True)] [Uri] $identityUrl, [Parameter(Mandatory = $True)] [string] $apiName, [Parameter(Mandatory = $True)] [string] $accessToken ) $apiExists = $false $url = "$($identityUrl.OriginalString.TrimEnd("/"))/api/apiresource/$apiName" $headers = @{"Accept" = "application/json"} if ($accessToken) { $headers.Add("Authorization", "Bearer $accessToken") } try { $getResponse = Invoke-RestMethod -Method Get -Uri $url -ContentType "application/json" -Headers $headers $apiExists = $true return $apiExists } catch { $exception = $_.Exception if ((Assert-WebExceptionType -exception $exception -typeCode 404)) { try { return $false } catch { $error = "Unknown error looking for api" $exception = $_.Exception if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw (New-Object -TypeName "System.Net.WebException" "There was an error looking for api $($apiResourceObject.name): $error. Registration lookup failure.", $exception) } } else { $error = "Unknown error looking for api" $exception = $_.Exception if ($null -ne $exception -and $null -ne $exception.Response) { $error = Get-ErrorFromResponse -response $exception.Response } throw ( New-Object -TypeName "System.Net.WebException" "There was an error looking for api $($apiResourceObject.name) with Fabric.Identity: $error, Registration lookup failure.", $exception) } } } <# .Synopsis checks the exception for type .Description checks in the exception for the typeCode Abstracted to facilitate exception inspection returns either $true or $false .Parameter exception an exception or object of some kind thrown .Parameter typeCode an exception code to find .Example Assert-WebExceptionType -exception $exception -typeCode 404 #> function Assert-WebExceptionType( $exception, $typeCode) { if ($null -ne $exception -and $exception.Response.StatusCode.value__ -eq $typeCode) { return $true } else { return $false } } <# .Synopsis INTERNAL: function to extract exception response messages .Description extracts the exception message if it exists Abstracted to facilitate exception inspection returns the response body (either an object or a string) .Parameter response the exception response .Example Get-ErrorFromResponse -response $exception.response #> function Get-ErrorFromResponse($response) { $result = $response.GetResponseStream() $reader = New-Object System.IO.StreamReader($result) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $responseBody = $reader.ReadToEnd(); return $responseBody } Export-ModuleMember -Function Get-AccessToken Export-ModuleMember -Function Get-FabricInstallerAccessToken Export-ModuleMember -Function Get-ClientRegistration Export-ModuleMember -Function New-ClientRegistration Export-ModuleMember -Function New-ClientCredentialsClientBody Export-ModuleMember -Function New-ImplicitClientBody Export-ModuleMember -Function New-HybridClientBody Export-ModuleMember -Function New-HybridPkceClientBody Export-ModuleMember -Function Edit-ClientRegistration Export-ModuleMember -Function Reset-ClientPassword Export-ModuleMember -Function Test-IsClientRegistered Export-ModuleMember -Function Get-ApiRegistration Export-ModuleMember -Function New-ApiRegistration Export-ModuleMember -Function New-ApiRegistrationBody Export-ModuleMember -Function Remove-ApiRegistration Export-ModuleMember -Function Edit-ApiRegistration Export-ModuleMember -Function Reset-ApiPassword Export-ModuleMember -Function Test-IsApiRegistered Export-ModuleMember -Function Assert-WebExceptionType # SIG # Begin signature block # MIIcRgYJKoZIhvcNAQcCoIIcNzCCHDMCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA8iMg2rXmZJ3iY # JB9nRcJnN1DBfaUrqq2kU/OWL6jINKCCCqAwggUwMIIEGKADAgECAhAECRgbX9W7 # ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBa # Fw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lD # ZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3 # DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/l # qJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fT # eyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqH # CN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+ # bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLo # LFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIB # yTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK # BggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9v # Y3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHow # eDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl # ZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwA # AgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAK # BghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0j # BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7s # DVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGS # dQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6 # r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo # +MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qz # sIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHq # aGxEMrJmoecYpJpkUe8wggVoMIIEUKADAgECAhAKRecO+XBAYPQ5XoaaebXrMA0G # CSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0 # IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwHhcNMTcwNDEzMDAwMDAw # WhcNMjAwNDE1MTIwMDAwWjCBpDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcw # FQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVSGVhbHRoIENhdGFseXN0 # LCBJbmMuMR4wHAYDVQQDExVIZWFsdGggQ2F0YWx5c3QsIEluYy4xLzAtBgkqhkiG # 9w0BCQEWIGFkbWluaXN0cmF0b3JAaGVhbHRoY2F0YWx5c3QuY29tMIIBIjANBgkq # hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv8AEfB5imOv8J17fvW8w+WKuE0keRub9 # 1+QzkiI+nSa9y2yADr/ZCEXqxGqDKdg47CjlvpOmKg8K88NPaTPvGN5fm7p7avmn # Cfp7IGXLGtutZ1RnFW2fYC8+kl86WinKVQ7eHLe7Rsvn9CyurIzttJpJcTikxqrr # U45yE8Iw/H9ziiwP+grfm8AiGN3C2vuxbhs8YwG2pbbn2aa5hN5q4bbFzoQ4xHGO # kFiqhRYVyGbVZNeoGTpkf/DNXJh07RuSDdcFXoh7whwwvfXhrk9Z5YzE6GEk2CUF # adTjqWHuGyfpBpY7bYZ8/mbDTmUqLNeGsTQrVmowv4r+usyK6lz6LwIDAQABo4IB # xTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYE # FDCXth9LjWUWNRWEPkEw5VZAVdBSMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK # BggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2Vy # dC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQu # ZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUwQzA3 # BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu # Y29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNpZ25p # bmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAkIewxl/k # WdhH2w7hIW0jT2WXhasjLk/UVeJtON2V7uj6J5/geg9huBlF9UDASBN9Po3sULeE # /WQ+Lxbd3BDLq+jcENPKdEE7v9NFOCzs142tBJ+tng5uSD4KCG7wStTggI8XElpu # 0uraecK21bq4T4A2uGXpruEVNdS8DkANh34AwLJWanhaavbqunHZMkjQU0oluktS # ikJ1BVeyROM0Xh11VBnM5nSftS4c8eC66ZXhsuc268wwzwb3eD81jKwXdli3SrvT # zFKtAFqzh2/1bVIceq+iT7zketpGuFTg3BOkhbiJhIEjAS9pA3v+tVKrWcdTp/HC # mT2XH0Xyeg2GhzGCEPwwghD4AgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV # BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0ECEApF # 5w75cEBg9Dlehpp5teswDQYJYIZIAWUDBAIBBQCgfDAQBgorBgEEAYI3AgEMMQIw # ADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYK # KwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg3PMNWHRM1nmKfMukWNu9m3o/W0wr # L3tvB9FAp+N3c38wDQYJKoZIhvcNAQEBBQAEggEApr3IT7KOarJr0Zz9VUZoHFrC # UzHsTToSbrirR0Weqrp5/OQaxaQaC0/zfzi6rNhA35lxEzMW5tKUd/4Tcgfe2U3h # 3i/0DpaI9T/Ret1RbeNQslMtpFp9TeRhDpOcZJvPcud/ME6ZO2+0ByDkuFuekIbt # OSN3cocq5FA1vOZg3RYMNJqcgfBT2qyYNTNklFJ5Dvs4SRmoWpmtsBAXn9497tEm # 0oJ5TjBV0oNc9E7rF6D060OtmaIdqx+j1I7ilqesPrAl+n2jbG0Fg0aDDS9iyqQV # Il3BOwLc+wBqJkOBjVlFVqb8dTC8ppKL1sgQpjv4FBfqeIK4RnWIHLXY2Y86X6GC # Dsgwgg7EBgorBgEEAYI3AwMBMYIOtDCCDrAGCSqGSIb3DQEHAqCCDqEwgg6dAgED # MQ8wDQYJYIZIAWUDBAIBBQAwdwYLKoZIhvcNAQkQAQSgaARmMGQCAQEGCWCGSAGG # /WwHATAxMA0GCWCGSAFlAwQCAQUABCBX6Lnwt8JD8HinEV/moh67L6+UR3Xz54rW # GPdR7AUnwgIQDnkCqVCeXGzsXt8DBfrwBBgPMjAxODA3MjAxNTM0MjBaoIILuzCC # BoIwggVqoAMCAQICEAnA/EbIBEITtVmLryhPTkEwDQYJKoZIhvcNAQELBQAwcjEL # MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 # LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElE # IFRpbWVzdGFtcGluZyBDQTAeFw0xNzAxMDQwMDAwMDBaFw0yODAxMTgwMDAwMDBa # MEwxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEqMCgGA1UEAxMhRGln # aUNlcnQgU0hBMiBUaW1lc3RhbXAgUmVzcG9uZGVyMIIBIjANBgkqhkiG9w0BAQEF # AAOCAQ8AMIIBCgKCAQEAnpWYajQ7cxuofvzHvilpicdoJkZfPY1ic4eBo6Gc8Ldb # JDdaktT0Wdd2ieTc1Sfw1Wa8Cu60KzFnrFjFSpFZK0UeCQHWZLNZ7o1mTfsjXswQ # DQuKZ+9SrqAIkMJS9/WotW6bLHud57U++3jNMlAYv0C1TIy7V/SgTxFFbEJCueWv # 1t/0p3wKaJYP0l8pV877HTL/9BGhEyL7Esvv11PS65fLoqwbHZ1YIVGCwsLe6is/ # LCKE0EPsOzs/R8T2VtxFN5i0a3S1Wa94V2nIDwkCeN3YU8GZ22DEnequr+B+hkpc # qVhhqF50igEoaHJOp4adtQJSh3BmSNOO74EkzNzYZQIDAQABo4IDODCCAzQwDgYD # VR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH # AwgwggG/BgNVHSAEggG2MIIBsjCCAaEGCWCGSAGG/WwHATCCAZIwKAYIKwYBBQUH # AgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwggFkBggrBgEFBQcCAjCC # AVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABp # AGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBw # AHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQ # AC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQBy # AHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0 # ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwBy # AHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBl # AG4AYwBlAC4wCwYJYIZIAYb9bAMVMB8GA1UdIwQYMBaAFPS24SAd/imu0uRhpbKi # JbLIFzVuMB0GA1UdDgQWBBThpzJK7gEhKH1U1fIHkm60Bw89hzBxBgNVHR8EajBo # MDKgMKAuhixodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLXRz # LmNybDAyoDCgLoYsaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJl # ZC10cy5jcmwwgYUGCCsGAQUFBwEBBHkwdzAkBggrBgEFBQcwAYYYaHR0cDovL29j # c3AuZGlnaWNlcnQuY29tME8GCCsGAQUFBzAChkNodHRwOi8vY2FjZXJ0cy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEVGltZXN0YW1waW5nQ0EuY3J0 # MA0GCSqGSIb3DQEBCwUAA4IBAQAe8EGCMq7t8bQ1E9xQwtWXriIinQ4OrzPTTP18 # v28BEaeUZSJcxiKhyIlSa5qMc1zZXj8y3hZgTIs2/TGZCr3BhLeNHe+JJhMFVvNH # zUdbrYSyOK9qI7VF4x6IMkaA0remmSL9wXjP9YvYDIwFCe5E5oDVbXDMn1MeJ90q # SN7ak2WtbmWjmafCQA5zzFhPj0Uo5byciOYozmBdLSVdi3MupQ1bUdqaTv9QBYko # 2vJ4u9JYeI1Ep6w6AJF4aYlkBNNdlt8qv/mlTCyT/+aK3YKs8dKzooaawVWJVmpH # P/rWM5VDNYkFeFo6adoiuARD029oNTZ6FD5F6Zhkhg8TDCZKMIIFMTCCBBmgAwIB # AgIQCqEl1tYyG35B5AXaNpfCFTANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJV # UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu # Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTYw # MTA3MTIwMDAwWhcNMzEwMTA3MTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UE # ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYD # VQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMIIB # IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvdAy7kvNj3/dqbqCmcU5VChX # tiNKxA4HRTNREH3Q+X1NaH7ntqD0jbOI5Je/YyGQmL8TvFfTw+F+CNZqFAA49y4e # O+7MpvYyWf5fZT/gm+vjRkcGGlV+Cyd+wKL1oODeIj8O/36V+/OjuiI+GKwR5PCZ # A207hXwJ0+5dyJoLVOOoCXFr4M8iEA91z3FyTgqt30A6XLdR4aF5FMZNJCMwXbzs # PGBqrC8HzP3w6kfZiFBe/WZuVmEnKYmEUeaC50ZQ/ZQqLKfkdT66mA+Ef58xFNat # 1fJky3seBdCEGXIX8RcG7z3N1k3vBkL9olMqT4UdxB08r8/arBD13ays6Vb/kwID # AQABo4IBzjCCAcowHQYDVR0OBBYEFPS24SAd/imu0uRhpbKiJbLIFzVuMB8GA1Ud # IwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMBIGA1UdEwEB/wQIMAYBAf8CAQAw # DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHkGCCsGAQUFBwEB # BG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsG # AQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1 # cmVkSURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsNC5k # aWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRo # dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0Eu # Y3JsMFAGA1UdIARJMEcwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIBFhxodHRw # czovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAsGCWCGSAGG/WwHATANBgkqhkiG9w0B # AQsFAAOCAQEAcZUS6VGHVmnN793afKpjerN4zwY3QITvS4S/ys8DAv3Fp8MOIEIs # r3fzKx8MIVoqtwU0HWqumfgnoma/Capg33akOpMP+LLR2HwZYuhegiUexLoceywh # 4tZbLBQ1QwRostt1AuByx5jWPGTlH0gQGF+JOGFNYkYkh2OMkVIsrymJ5Xgf1gsU # pYDXEkdws3XVk4WTfraSZ/tTYYmo9WuWwPRYaQ18yAGxuSh1t5ljhSKMYcp5lH5Z # /IwP42+1ASa2bKXuh1Eh5Fhgm7oMLSttosR+u8QlK0cCCHxJrhO24XxCQijGGFbP # QTS2Zl22dHv1VjMiLyI2skuiSpXY9aaOUjGCAk0wggJJAgEBMIGGMHIxCzAJBgNV # BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp # Y2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1l # c3RhbXBpbmcgQ0ECEAnA/EbIBEITtVmLryhPTkEwDQYJYIZIAWUDBAIBBQCggZgw # GgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0xODA3 # MjAxNTM0MjBaMC8GCSqGSIb3DQEJBDEiBCBqFNYxqXS0553IHce0ZSI9heO1mNiP # hWAq+W2MYtDmBjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBRAAZFHXJiJHeuhBK9H # CRtettTLyzANBgkqhkiG9w0BAQEFAASCAQBsTwSzxWsmq7PQ5sSv9wmuYbeZReYY # y0aHasWGpcIdxkaUcpxtmGQ8Vym3WmCrEYaipze2hEGbh8tB7nluHgNEO47N2Jls # F4j58CHVddnZRga8BCrzzDRUmvy5l93EnDkwoybzk2arYk9c6qS4zN6maxk+Cpnw # gDSpDUK6IzqQKhbmUF88NpBHyyk4gIZ/be0YXEaRHNSiX/5bSrnBMmjnAYf4scBl # h7yaIZJw+4PldSN9aSgUzHDYZptYsP7zj4C3EZM0mPVaaupTpFSj88GTWJk761OU # 2HlJ+7zVfPel/EuxXPLKSkpXHg0rXFJghUrgUX+qrIZv3qB/e26EjJPC # SIG # End signature block |