Extensions/PowerShellCmdletsExtensions.Tests.ps1
Param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Server, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $User, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Password ) BeforeAll { function Test-ObjectsAreEqual { [CmdletBinding()] [OutputType([bool])] param ( [Parameter(Mandatory = $true)] [AllowNull()] [object] $Actual, [Parameter(Mandatory = $true)] [AllowNull()] [object] $Expected, [Parameter(Mandatory = $false)] [type] $ExpectedType, [Parameter(Mandatory = $false)] [int] $Depth = 10 ) process { $result = $true if ($null -ne $ExpectedType -and $Actual -IsNot $ExpectedType) { Write-Verbose -Message "The actual object's type [$($Actual.GetType())] doesn't match the expected one [$ExpectedType]" $result = $false } elseif ($Expected.GetType().IsPrimitive -or $Expected -Is [string]) { Write-Verbose -Message "Primitive type [$($Expected.GetType())] found" $result = ($Actual -eq $Expected) } else { Write-Verbose -Message "Complex type [$($Expected.GetType())] found" $expectedProperties = $Expected | Get-Member -MemberType Properties foreach ($property in $expectedProperties) { Write-Verbose -Message "Asserting property $($property.Name)" $expectedValue = $Expected | Select-Object -ExpandProperty $property.Name $actualValue = $Actual | Select-Object -ExpandProperty $property.Name Write-Verbose -Message "Expected value = $expectedValue" Write-Verbose -Message "Actual value = $actualValue" if ($expectedValue -eq $actualValue) { Write-Verbose -Message "Values are equal" continue } elseif ($Depth -eq -1) { $result = $true } else { $areObjectsEqual = Test-ObjectsAreEqual -Expected $expectedValue -Actual $actualValue -Depth ($Depth - 1) if (!$areObjectsEqual) { $result = $false break } } } } return $result } } } Describe 'PowerShell Cmdlets Extensions Tests' { BeforeAll { . (Join-Path -Path $PSScriptRoot -ChildPath 'PowerShellCmdletsExtensions.ps1') $certificateThumbprint = $null if ($Global:PSVersionTable.PSEdition -eq 'Desktop') { $initialMaxServicePointIdleTime = [System.Net.ServicePointManager]::MaxServicePointIdleTime $maxServicePointIdleTimeInMilliseconds = 1 [System.Net.ServicePointManager]::MaxServicePointIdleTime = $maxServicePointIdleTimeInMilliseconds $certificateThumbprint = Get-TlsCertificateThumbprintFromRemoteHost -RemoteHostName $Server } } AfterAll { if ($Global:PSVersionTable.PSEdition -eq 'Desktop') { [System.Net.ServicePointManager]::MaxServicePointIdleTime = $initialMaxServicePointIdleTime } } Context 'Invoke-WebRequestX Tests' { It 'Should skip all Certificate checks when SkipCertificateCheck parameter is passed with $true value' { # Arrange $localVarBytes = [System.Text.Encoding]::UTF8.GetBytes($User + ':' + $Password) $localVarBase64Text = [Convert]::ToBase64String($localVarBytes) $headers = @{ 'Authorization' = "Basic $localVarBase64Text" 'Accept' = 'application/json' } $secureStringPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $secureStringPassword $invokeWebRequestParams = @{ 'Uri' = "https://$Server/api/session" 'Method' = 'POST' 'Headers' = $headers 'ErrorAction' = 'Stop' 'UseBasicParsing' = $true 'Credential' = $credential } # Act if ($Global:PSVersionTable.PSEdition -eq 'Desktop') { [CustomCertificatesValidator]::AddCertificateToCache($certificateThumbprint) } $result = Invoke-WebRequestX -InvokeParams $invokeWebRequestParams -SkipCertificateCheck $true # Assert $result | Should -Not -Be $null $result.StatusCode | Should -Be 201 $result.Content | Should -Not -BeNullOrEmpty $result.Headers | Should -Not -Be $null $result.Headers['Content-Type'] | Should -Be 'application/json' $result.Headers['vmware-api-session-id'] | Should -Not -BeNullOrEmpty } It 'Should throw an exception with the expected message when SkipCertificateCheck parameter is passed with $false value' { # Arrange $localVarBytes = [System.Text.Encoding]::UTF8.GetBytes($User + ':' + $Password) $localVarBase64Text = [Convert]::ToBase64String($localVarBytes) $headers = @{ 'Authorization' = "Basic $localVarBase64Text" 'Accept' = 'application/json' } $secureStringPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $secureStringPassword $invokeWebRequestParams = @{ 'Uri' = "https://$Server/api/session" 'Method' = 'POST' 'Headers' = $headers 'ErrorAction' = 'Stop' 'UseBasicParsing' = $true 'Credential' = $credential } if ($Global:PSVersionTable.PSEdition -eq 'Desktop') { [CustomCertificatesValidator]::RemoveCertificateFromCache($certificateThumbprint) | Out-Null } $corePowerShellExpectedExceptionMessage = 'The SSL connection could not be established, see inner exception.' $desktopPowerShellExpectedExceptionMessage = 'The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.' $expectedInnerExceptionMessage = 'The remote certificate is invalid according to the validation procedure*' # Act $actualError = $null try { Invoke-WebRequestX -InvokeParams $invokeWebRequestParams -SkipCertificateCheck $false } catch { $actualError = $_ } # Assert $actualError | Should -Not -Be $null $actualError.Exception | Should -Not -Be $null if ($Global:PSVersionTable.PSEdition -eq 'Core') { $actualError.Exception.Message | Should -Be $corePowerShellExpectedExceptionMessage } else { $actualError.Exception.Message | Should -Be $desktopPowerShellExpectedExceptionMessage } $actualError.Exception.InnerException | Should -Not -Be $null $actualError.Exception.InnerException.Message | Should -BeLike $expectedInnerExceptionMessage } } Context 'Invoke-RestMethodX Tests' { It 'Should skip all Certificate checks when SkipCertificateCheck parameter is passed with $true value' { # Arrange $localVarBytes = [System.Text.Encoding]::UTF8.GetBytes($User + ':' + $Password) $localVarBase64Text = [Convert]::ToBase64String($localVarBytes) $headers = @{ 'Authorization' = "Basic $localVarBase64Text" 'Accept' = 'application/json' } $secureStringPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $secureStringPassword $invokeWebRequestParams = @{ 'Uri' = "https://$Server/api/session" 'Method' = 'POST' 'Headers' = $headers 'ErrorAction' = 'Stop' 'UseBasicParsing' = $true 'Credential' = $credential } if ($Global:PSVersionTable.PSEdition -eq 'Desktop') { [CustomCertificatesValidator]::AddCertificateToCache($certificateThumbprint) } $sessionId = Invoke-WebRequestX -InvokeParams $invokeWebRequestParams -SkipCertificateCheck $true $headers['vmware-api-session-id'] = $sessionId -Replace '"', '' $invokeRestMethodParams = @{ 'Uri' = "https://$Server/rest/appliance/system/version" 'Method' = 'GET' 'Headers' = $headers } # Act $result = Invoke-RestMethodX -InvokeParams $invokeRestMethodParams -SkipCertificateCheck $true # Assert $result | Should -Not -Be $null $result.value | Should -Not -Be $null $result.value.version | Should -Not -Be $null [System.Version] $result.value.version | Should -Not -Be $null } It 'Should throw an exception with the expected message when SkipCertificateCheck parameter is passed with $false value' { # Arrange $localVarBytes = [System.Text.Encoding]::UTF8.GetBytes($User + ':' + $Password) $localVarBase64Text = [Convert]::ToBase64String($localVarBytes) $headers = @{ 'Authorization' = "Basic $localVarBase64Text" 'Accept' = 'application/json' } $invokeRestMethodParams = @{ 'Uri' = "https://$Server/rest/appliance/system/version" 'Method' = 'GET' 'Headers' = $headers } if ($Global:PSVersionTable.PSEdition -eq 'Desktop') { [CustomCertificatesValidator]::RemoveCertificateFromCache($certificateThumbprint) | Out-Null } $corePowerShellExpectedExceptionMessage = 'The SSL connection could not be established, see inner exception.' $desktopPowerShellExpectedExceptionMessage = 'The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.' $expectedInnerExceptionMessage = 'The remote certificate is invalid according to the validation procedure*' # Act $actualError = $null try { Invoke-RestMethodX -InvokeParams $invokeRestMethodParams -SkipCertificateCheck $false } catch { $actualError = $_ } # Assert $actualError | Should -Not -Be $null $actualError.Exception | Should -Not -Be $null if ($Global:PSVersionTable.PSEdition -eq 'Core') { $actualError.Exception.Message | Should -Be $corePowerShellExpectedExceptionMessage } else { $actualError.Exception.Message | Should -Be $desktopPowerShellExpectedExceptionMessage } $actualError.Exception.InnerException | Should -Not -Be $null $actualError.Exception.InnerException.Message | Should -BeLike $expectedInnerExceptionMessage } } Context 'Get-PlainTextPassword Tests' { It 'Should retrieve the password as plain text from the SecureString' { # Arrange $expected = 'MyTestPassword' $secureStringPassword = ConvertTo-SecureString -String $expected -AsPlainText -Force # Act $actual = Get-PlainTextPassword -Password $secureStringPassword # Assert $actual | Should -Be $expected } } Context 'ConvertFrom-JsonX Tests' { It 'Should convert the specified JSON to the expected PSCustomObject' { # Arrange $vmInfoJson = @" { "instant_clone_frozen": false, "guest_OS": "WINDOWS_SERVER_2019", "power_state": "POWERED_OFF", "name": "testvm", "boot_devices": [], "custom_vm_props": ["custom_vm_prop_one", "custom_vm_prop_two"], "custom_vm_array": ["custom_vm_array_element"], "hardware": { "upgrade_policy": "NEVER", "upgrade_status": "NONE", "version": "VMX_19" }, "cdroms": { "16000": { "start_connected": false, "backing": { "device_access_type": "PASSTHRU", "type": "CLIENT_DEVICE" }, "allow_guest_control": true, "label": "CD/DVD drive 1", "state": "NOT_CONNECTED", "type": "SATA", "sata": { "bus": 0, "unit": 0 } } } } "@ $expected = [PSCustomObject] @{ 'instant_clone_frozen' = $false 'guest_OS' = 'WINDOWS_SERVER_2019' 'power_state' = 'POWERED_OFF' 'name' = 'testvm' 'boot_devices' = @() 'hardware' = [PSCustomObject] @{ 'upgrade_policy' = 'NEVER' 'upgrade_status' = 'NONE' 'version' = 'VMX_19' } 'cdroms' = [PSCustomObject] @{ '16000' = [PSCustomObject] @{ 'start_connected' = $false 'backing' = [PSCustomObject] @{ 'device_access_type' = 'PASSTHRU' 'type' = 'CLIENT_DEVICE' } 'allow_guest_control' = $true 'label' = 'CD/DVD drive 1' 'state' = 'NOT_CONNECTED' 'type' = 'SATA' 'sata' = [PSCustomObject] @{ 'bus' = 0 'unit' = 0 } } } 'custom_vm_props' = @( "custom_vm_prop_one", "custom_vm_prop_two" ) 'custom_vm_array' = @("custom_vm_array_element") } # Act $actual = ConvertFrom-JsonX -InputObject $vmInfoJson -Depth 100 # Assert Test-ObjectsAreEqual $actual $expected | Should -BeTrue } } } # SIG # Begin signature block # MIIexwYJKoZIhvcNAQcCoIIeuDCCHrQCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDu63KScQ0w2hBJ # 05jUMpGtEbFlU73lYU6nQxd2OjUmdaCCDdowggawMIIEmKADAgECAhAIrUCyYNKc # TJ9ezam9k67ZMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV # BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0z # NjA0MjgyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg # UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw # ggIKAoICAQDVtC9C0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0 # JAfhS0/TeEP0F9ce2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJr # Q5qZ8sU7H/Lvy0daE6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhF # LqGfLOEYwhrMxe6TSXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+F # LEikVoQ11vkunKoAFdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh # 3K3kGKDYwSNHR7OhD26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJ # wZPt4bRc4G/rJvmM1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQay # g9Rc9hUZTO1i4F4z8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbI # YViY9XwCFjyDKK05huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchAp # QfDVxW0mdmgRQRNYmtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRro # OBl8ZhzNeDhFMJlP/2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IB # WTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+ # YXsIiGX0TkIwHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0P # AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAk # BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAC # hjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v # dEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5j # b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAED # MAgGBmeBDAEEATANBgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql # +Eg08yy25nRm95RysQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFF # UP2cvbaF4HZ+N3HLIvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1h # mYFW9snjdufE5BtfQ/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3Ryw # YFzzDaju4ImhvTnhOE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5Ubdld # AhQfQDN8A+KVssIhdXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw # 8MzK7/0pNVwfiThV9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnP # LqR0kq3bPKSchh/jwVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatE # QOON8BUozu3xGFYHKi8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bn # KD+sEq6lLyJsQfmCXBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQji # WQ1tygVQK+pKHJ6l/aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbq # yK+p/pQd52MbOoZWeE4wggciMIIFCqADAgECAhAOxvKydqFGoH0ObZNXteEIMA0G # CSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg # UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjEwODEwMDAwMDAwWhcNMjMwODEw # MjM1OTU5WjCBhzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQ # BgNVBAcTCVBhbG8gQWx0bzEVMBMGA1UEChMMVk13YXJlLCBJbmMuMRUwEwYDVQQD # EwxWTXdhcmUsIEluYy4xITAfBgkqhkiG9w0BCQEWEm5vcmVwbHlAdm13YXJlLmNv # bTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAMD6lJG8OWkM12huIQpO # /q9JnhhhW5UyW9if3/UnoFY3oqmp0JYX/ZrXogUHYXmbt2gk01zz2P5Z89mM4gqR # bGYC2tx+Lez4GxVkyslVPI3PXYcYSaRp39JsF3yYifnp9R+ON8O3Gf5/4EaFmbeT # ElDCFBfExPMqtSvPZDqekodzX+4SK1PIZxCyR3gml8R3/wzhb6Li0mG7l0evQUD0 # FQAbKJMlBk863apeX4ALFZtrnCpnMlOjRb85LsjV5Ku4OhxQi1jlf8wR+za9C3DU # ki60/yiWPu+XXwEUqGInIihECBbp7hfFWrnCCaOgahsVpgz8kKg/XN4OFq7rbh4q # 5IkTauqFhHaE7HKM5bbIBkZ+YJs2SYvu7aHjw4Z8aRjaIbXhI1G+NtaNY7kSRrE4 # fAyC2X2zV5i4a0AuAMM40C1Wm3gTaNtRTHnka/pbynUlFjP+KqAZhOniJg4AUfjX # sG+PG1LH2+w/sfDl1A8liXSZU1qJtUs3wBQFoSGEaGBeDQIDAQABo4ICJTCCAiEw # HwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFIhC+HL9 # QlvsWsztP/I5wYwdfCFNMB0GA1UdEQQWMBSBEm5vcmVwbHlAdm13YXJlLmNvbTAO # BgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCB # qjBToFGgT4ZNaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3Rl # ZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0 # dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu # aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQQB # MCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCBlAYI # KwYBBQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0 # LmNvbTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5j # cnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEACQAYaQI6Nt2KgxdN # 6qqfcHB33EZRSXkvs8O9iPZkdDjEx+2fgbBPLUvk9A7T8mRw7brbcJv4PLTYJDFo # c5mlcmG7/5zwTOuIs2nBGXc/uxCnyW8p7kD4Y0JxPKEVQoIQ8lJS9Uy/hBjyakeV # ef982JyzvDbOlLBy6AS3ZpXVkRY5y3Va+3v0R/0xJ+JRxUicQhiZRidq2TCiWEas # d+tLL6jrKaBO+rmP52IM4eS9d4Yids7ogKEBAlJi0NbvuKO0CkgOlFjp1tOvD4sQ # taHIMmqi40p4Tjyf/sY6yGjROXbMeeF1vlwbBAASPWpQuEIxrNHoVN30YfJyuOWj # zdiJUTpeLn9XdjM3UlhfaHP+oIAKcmkd33c40SFRlQG9+P9Wlm7TcPxGU4wzXI8n # Cw/h235jFlAAiWq9L2r7Un7YduqsheJVpGoXmRXJH0T2G2eNFS5/+2sLn98kN2Cn # J7j6C242onjkZuGL2/+gqx8m5Jbpu9P4IAeTC1He/mX9j6XpIu+7uBoRVwuWD1i0 # N5SiUz7Lfnbr6Q1tHMXKDLFdwVKZos2AKEZhv4SU0WvenMJKDgkkhVeHPHbTahQf # P1MetR8tdRs7uyTWAjPK5xf5DLEkXbMrUkpJ089fPvAGVHBcHRMqFA5egexOb6sj # tKncUjJ1xAAtAExGdCh6VD2U5iYxghBDMIIQPwIBATB9MGkxCzAJBgNVBAYTAlVT # MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1 # c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEA7G # 8rJ2oUagfQ5tk1e14QgwDQYJYIZIAWUDBAIBBQCggZYwGQYJKoZIhvcNAQkDMQwG # CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwKgYKKwYB # BAGCNwIBDDEcMBqhGIAWaHR0cDovL3d3dy52bXdhcmUuY29tLzAvBgkqhkiG9w0B # CQQxIgQgQ1SATk3LEqVYSVa4pSG7x3SH4yz1E3MjaKOug8ovJ5wwDQYJKoZIhvcN # AQEBBQAEggGAvvW8iPhGTLAGLrWhGpEbPhOWuolfnC0Kep2ZuEmcRsX2NDI+ZL3j # wuo+7rd/rwOBZ1ELmHkrXQPsXo7W54qltXoSzG0V8DRC5qIx9TXaO5oI5AHjk+qN # mit3RBuJlL9hoMd7AIqKTdTin9XAtj8GFcfoa6qkQQqqVV9bnTuApBalwOqO2BgD # 1PY4IUlC+tDy2b28R6bKHVmiT5S5Q+HqqSX/QweDLsmU8wBI4ZB4LqnpU1ZNvtRs # bJbecFGMYGGJ0aW4OIap9M6S+wZf9agvoj2WPobeCqtMMbdJXdBvVSVvQBrV2fBg # wCAXlTk4kuTRGq7bg7cZFCCejcoCW6fAPw7IEN704ekF5OjaHXEjcqOYs2+iktdg # qygftPAmfMtl91HuNcmkVXQkGF9WsFTd/NEt1OhbHShOdUcoCObMJVruo0gjgEVv # ovFkRlOTbt9C7O5sva7tjG1Mzv8Szki9Ix7tmMugk9QC9fxfPm+/541tA+SlJ2B1 # gXAKPkHwmZS8oYINfjCCDXoGCisGAQQBgjcDAwExgg1qMIINZgYJKoZIhvcNAQcC # oIINVzCCDVMCAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0BCRABBKBpBGcw # ZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIKjicO2YVUOavqmZyFAy # 8GNJS7OOs18uY4J5kRbEAhqbAhEAtlQlWf2Wdu1CFXElRUHcNRgPMjAyMTA5MTMx # NjA0MDhaoIIKNzCCBP4wggPmoAMCAQICEA1CSuC+Ooj/YEAhzhQA8N0wDQYJKoZI # hvcNAQELBQAwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ # MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hB # MiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBDQTAeFw0yMTAxMDEwMDAwMDBaFw0z # MTAxMDYwMDAwMDBaMEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjEgMB4GA1UEAxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjEwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDC5mGEZ8WK9Q0IpEXKY2tR1zoRQr0KdXVN # lLQMULUmEP4dyG+RawyW5xpcSO9E5b+bYc0VkWJauP9nC5xj/TZqgfop+N0rcIXe # AhjzeG28ffnHbQk9vmp2h+mKvfiEXR52yeTGdnY6U9HR01o2j8aj4S8bOrdh1nPs # Tm0zinxdRS1LsVDmQTo3VobckyON91Al6GTm3dOPL1e1hyDrDo4s1SPa9E14RuMD # gzEpSlwMMYpKjIjF9zBa+RSvFV9sQ0kJ/SYjU/aNY+gaq1uxHTDCm2mCtNv8VlS8 # H6GHq756WwogL0sJyZWnjbL61mOLTqVyHO6fegFz+BnW/g1JhL0BAgMBAAGjggG4 # MIIBtDAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAK # BggrBgEFBQcDCDBBBgNVHSAEOjA4MDYGCWCGSAGG/WwHATApMCcGCCsGAQUFBwIB # FhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHwYDVR0jBBgwFoAU9LbhIB3+ # Ka7S5GGlsqIlssgXNW4wHQYDVR0OBBYEFDZEho6kurBmvrwoLR1ENt3janq8MHEG # A1UdHwRqMGgwMqAwoC6GLGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFz # c3VyZWQtdHMuY3JsMDKgMKAuhixodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hh # Mi1hc3N1cmVkLXRzLmNybDCBhQYIKwYBBQUHAQEEeTB3MCQGCCsGAQUFBzABhhho # dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wTwYIKwYBBQUHMAKGQ2h0dHA6Ly9jYWNl # cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURUaW1lc3RhbXBp # bmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggEBAEgc3LXpmiO85xrnIA6OZ0b9QnJR # dAojR6OrktIlxHBZvhSg5SeBpU0UFRkHefDRBMOG2Tu9/kQCZk3taaQP9rhwz2Lo # 9VFKeHk2eie38+dSn5On7UOee+e03UEiifuHokYDTvz0/rdkd2NfI1Jpg4L6GlPt # kMyNoRdzDfTzZTlwS/Oc1np72gy8PTLQG8v1Yfx1CAB2vIEO+MDhXM/EEXLnG2RJ # 2CKadRVC9S0yOIHa9GCiurRS+1zgYSQlT7LfySmoc0NR2r1j1h9bm/cuG08THfdK # DXF+l7f0P4TrweOjSaH6zqe/Vs+6WXZhiV9+p7SOZ3j5NpjhyyjaW4emii8wggUx # MIIEGaADAgECAhAKoSXW1jIbfkHkBdo2l8IVMA0GCSqGSIb3DQEBCwUAMGUxCzAJ # BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k # aWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBD # QTAeFw0xNjAxMDcxMjAwMDBaFw0zMTAxMDcxMjAwMDBaMHIxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBp # bmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC90DLuS82Pf92p # uoKZxTlUKFe2I0rEDgdFM1EQfdD5fU1ofue2oPSNs4jkl79jIZCYvxO8V9PD4X4I # 1moUADj3Lh477sym9jJZ/l9lP+Cb6+NGRwYaVX4LJ37AovWg4N4iPw7/fpX786O6 # Ij4YrBHk8JkDbTuFfAnT7l3ImgtU46gJcWvgzyIQD3XPcXJOCq3fQDpct1HhoXkU # xk0kIzBdvOw8YGqsLwfM/fDqR9mIUF79Zm5WYScpiYRR5oLnRlD9lCosp+R1PrqY # D4R/nzEU1q3V8mTLex4F0IQZchfxFwbvPc3WTe8GQv2iUypPhR3EHTyvz9qsEPXd # rKzpVv+TAgMBAAGjggHOMIIByjAdBgNVHQ4EFgQU9LbhIB3+Ka7S5GGlsqIlssgX # NW4wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wEgYDVR0TAQH/BAgw # BgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgweQYI # KwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j # b20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp # Q2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6 # Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmww # OqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ # RFJvb3RDQS5jcmwwUAYDVR0gBEkwRzA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUH # AgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCwYJYIZIAYb9bAcBMA0G # CSqGSIb3DQEBCwUAA4IBAQBxlRLpUYdWac3v3dp8qmN6s3jPBjdAhO9LhL/KzwMC # /cWnww4gQiyvd/MrHwwhWiq3BTQdaq6Z+CeiZr8JqmDfdqQ6kw/4stHYfBli6F6C # JR7Euhx7LCHi1lssFDVDBGiy23UC4HLHmNY8ZOUfSBAYX4k4YU1iRiSHY4yRUiyv # KYnleB/WCxSlgNcSR3CzddWThZN+tpJn+1Nhiaj1a5bA9FhpDXzIAbG5KHW3mWOF # IoxhynmUfln8jA/jb7UBJrZspe6HUSHkWGCbugwtK22ixH67xCUrRwIIfEmuE7bh # fEJCKMYYVs9BNLZmXbZ0e/VWMyIvIjayS6JKldj1po5SMYIChjCCAoICAQEwgYYw # cjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVk # IElEIFRpbWVzdGFtcGluZyBDQQIQDUJK4L46iP9gQCHOFADw3TANBglghkgBZQME # AgEFAKCB0TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkF # MQ8XDTIxMDkxMzE2MDQwOFowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU4deCqOGR # vu9ryhaRtaq0lKYkm/MwLwYJKoZIhvcNAQkEMSIEIC8DsxfnhAT4yV2pC4z00LkT # CdwhzDFjIk9SdL5eG1tsMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEILMQkAa8CtmD # B5FXKeBEA0Fcg+MpK2FPJpZMjTVx7PWpMA0GCSqGSIb3DQEBAQUABIIBAEfp4Vqj # KelDmnsLyhJaSG696/ff3nd57bFa0iz0WFgZc3BDNYmREy2IQJcJFnnHEkadz9h1 # 3tq2qX3DDIOgmvA/unVIfKuRQ//3iFANpvtkA3HPs/D/SRD6gNXPMScEKamU3xcG # EmPyhI8/ijPuP0R3s8KSQJueXGEKZetVfshZcvqBOSIbdOqGHa+I5GrLI2MSCA8H # NdKgLwWaSmvAwQ3mkbQ9BwCSLy9Yx0YPpUR69tuUmpwYt3gx6w6gdt9g7bn9hLrT # LIOktk40Czq4Ob0QXcHX2a3MIVCh9PXm20gaeuG64aHixv5yl2VhKByqKOCRl0Md # a0li9WPewyNi7e4= # SIG # End signature block |