Framework/BugLog/BugLogHelper.ps1
Set-StrictMode -Version Latest class BugLogHelper { hidden static [BugLogHelper] $BugLogHelperInstance; hidden [bool] $UseAzureStorageAccount; hidden [string] $OrganizationName; hidden [string] $StorageAccount; hidden [object] $StorageAccountCtx; hidden [string] $StorageRG; hidden [bool] $errorMsgDisplayed = $false hidden [string] $SharedKey hidden [object] $hmacsha BugLogHelper([string] $orgName) { $this.OrganizationName = $orgName; $this.StorageAccount = $env:StorageName; $this.StorageRG = $env:StorageRG; #get storage details if ($this.StorageRG -and $this.StorageAccount) { $keys = Get-AzStorageAccountKey -ResourceGroupName $this.StorageRG -Name $this.StorageAccount $StorageContext = New-AzStorageContext -StorageAccountName $this.StorageAccount -StorageAccountKey $keys[0].Value -Protocol Https $this.SharedKey = $keys[0].Value; $this.StorageAccountCtx = $StorageContext.Context; $this.hmacsha = New-Object System.Security.Cryptography.HMACSHA256 $this.hmacsha.key = [Convert]::FromBase64String($this.SharedKey) } } #Return BugLogHelper instance hidden static [BugLogHelper] GetInstance([string] $orgName) { [BugLogHelper]::BugLogHelperInstance = [BugLogHelper]::new($orgName) return [BugLogHelper]::BugLogHelperInstance } #function to search for existing bugs based on hash id hidden [object] GetWorkItemByHashAzureTable([string] $hash, [string] $projectName, [string] $reactiveOldBug) { #get table filter by name $tableName = $this.GetTableName(); $bugObj = @(@{}); $bugObj[0].results = @(); try { #get storage table data. $azTableBugInfo = @(); $azTableBugInfo += $this.GetTableEntity($tableName, $hash); if ($azTableBugInfo -and $azTableBugInfo.count -gt 0) { $adoBugId = $azTableBugInfo[0].ADOBugId; $uri = "https://dev.azure.com/$($this.OrganizationName)/$projectName/_apis/wit/workitems/{0}?api-version=6.0" -f $adoBugId; $response = [WebRequestHelper]::InvokeGetWebRequest($uri); if($response -and ($response.count -gt 0) -and ($response.fields."System.State" -ne "Closed")) { #check if org policy wants to reactivate resolved bugs. #if status is not 'Resolve', send response. #if status is 'Resolved' and ReactiveOldBug flag is true, then send response. (when response goes empty we add new bug) if (($response.fields."System.State" -ne "Resolved") -or ($response.fields."System.State" -eq "Resolved" -and $reactiveOldBug -eq "ReactiveOldBug") ) { $bugObj[0].results += $response; } } else { #if bug state is closed on the ADO side and isDeleted is 'N' in azuretable then update azure table -> set isdeleted ='Y' #$isDeleted = $this.DeleteTableEntity($tableName, $hash, $adoBugId); #if ($isDeleted -eq $true) { # $this.AddDataInTable($tableName, $hash, $adoBugId, $projectName, "Y"); #} $isUpdated = $this.UpdateTableEntity($tableName, $hash, $adoBugId, $projectName); } return $bugObj; } } catch { Write-Host $_ Write-Host "Could not access storage account." -ForegroundColor Red } return $bugObj; } hidden [bool] InsertBugInfoInTable([string] $hash, [string] $projectName, [string] $ADOBugId) { try { $tableName = $this.GetTableName(); #Get table filterd by name. $storageTables = @(); $storageTables += Get-AzStorageTable -Context $this.StorageAccountCtx | Select Name; #create table if table not found. if ( !$storageTables -or ($storageTables.count -eq 0) -or !($storageTables.Name -eq $tableName) ) { New-AzStorageTable $tableName -Context $this.StorageAccountCtx; } $isDataAddedInTable = $this.AddDataInTable($tableName, $hash, $ADOBugId, $projectName, "N") return $isDataAddedInTable; } catch { return $false; } return $false } hidden [object] GetTableEntity($tableName, $hash) { try { $query = 'ADOScannerHashId eq ''{0}'' and IsDeleted eq ''N''' -f $hash; $resource = '$filter='+[System.Web.HttpUtility]::UrlEncode($query); $table_url = "https://{0}.table.core.windows.net/{1}?{2}" -f $this.StorageAccount, $tableName, $resource $headers = $this.GetHeader($tableName) $item = Invoke-RestMethod -Method Get -Uri $table_url -Headers $headers -ContentType "application/json" return $item.value; } catch { #Write-Host $_ Write-Host "Could not fetch the entry for partition key [$hash] in the table storage or the table was not found."; return $null } } hidden [bool] AddDataInTable($tableName, $hash, $ADOBugId, $projectName, $isDeleted) { $partitionKey = $hash; $rowKey = $hash + "_" + $ADOBugId; try { #Add data in table. $entity = @{"PartitionKey" = $partitionKey; "RowKey" = $rowKey; "ADOBugId" = $ADOBugId; "ADOScannerHashId" = $hash; "IsDeleted" = $isDeleted; "ProjectName" = $projectName}; $table_url = "https://{0}.table.core.windows.net/{1}" -f $this.StorageAccount, $tableName $headers = $this.GetHeader($tableName); $body = $entity | ConvertTo-Json $item = Invoke-RestMethod -Method POST -Uri $table_url -Headers $headers -Body $body -ContentType "application/json" return $true; } catch { Write-Host $_ Write-Host "Could not push an entry in the table for row key [$rowKey]"; return $false; } } hidden [bool] UpdateTableEntity($tableName, $hash, $ADOBugId, $projectName) { $PartitionKey = $hash; $Rowkey = $hash + "_" + $ADOBugId; try { #Update data in table. $entity = @{"ADOBugId" = $ADOBugId; "ADOScannerHashId" = $hash; "IsDeleted" = "Y"; "ProjectName" = $projectName}; $body = $entity | ConvertTo-Json $version = "2017-04-17" $resource = "$tableName(PartitionKey='$PartitionKey',RowKey='$Rowkey')" $table_url = "https://$($this.StorageAccount).table.core.windows.net/$resource" $GMTTime = (Get-Date).ToUniversalTime().toString('R') $stringToSign = "$GMTTime`n/$($this.StorageAccount)/$resource" $signature = $this.hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign)) $signature = [Convert]::ToBase64String($signature) $body = $entity | ConvertTo-Json $headers = @{ 'x-ms-date' = $GMTTime Authorization = "SharedKeyLite " + $this.StorageAccount + ":" + $signature "x-ms-version" = $version Accept = "application/json;odata=minimalmetadata" 'If-Match' = "*" } Invoke-RestMethod -Method PUT -Uri $table_url -Headers $headers -Body $body -ContentType "application/json;odata=minimalmetadata" return $true; } catch { Write-Host $_ Write-Host "Could not update entry in the table for row key [$RowKey]"; return $false; } } hidden [bool] DeleteTableEntity($tableName, $hash, $ADOBugId) { $PartitionKey = $hash; $Rowkey = $hash + "_" + $ADOBugId; try { $version = "2017-04-17" $resource = "$tableName(PartitionKey='$PartitionKey',RowKey='$Rowkey')" $table_url = "https://$($this.StorageAccount).table.core.windows.net/$resource" $GMTTime = (Get-Date).ToUniversalTime().toString('R') $stringToSign = "$GMTTime`n/$($this.StorageAccount)/$resource" $signature = $this.hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign)) $signature = [Convert]::ToBase64String($signature) $headers = @{ 'x-ms-date' = $GMTTime Authorization = "SharedKeyLite " + $($this.StorageAccount) + ":" + $signature "x-ms-version" = $version Accept = "application/json;odata=minimalmetadata" 'If-Match' = "*" } $item = Invoke-RestMethod -Method DELETE -Uri $table_url -Headers $headers -ContentType application/http return $true } catch { Write-Host $_ Write-Host "Could not delete the entry for row key [$Rowkey] in the table storage."; return $false; } } hidden [object] GetHeader($tableName) { $version = "2017-07-29" $GMTTime = (Get-Date).ToUniversalTime().toString('R') $stringToSign = "$GMTTime`n/$($this.StorageAccount)/$tableName" $signature = $this.hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign)) $signature = [Convert]::ToBase64String($signature) $headers = @{ 'x-ms-date' = $GMTTime Authorization = "SharedKeyLite " + $this.StorageAccount + ":" + $signature "x-ms-version" = $version Accept = "application/json;odata=minimalmetadata" } return $headers } hidden [object[]] GetTableEntityAndCloseBug([string] $hash) { #get table filter by name $tableName = $this.GetTableName(); try { #Get clouddata to do perform read/write operations on the table $azTableBugInfo = @(); $query = '({0}) and IsDeleted eq ''N''' -f $hash; $resource = '$filter='+[System.Web.HttpUtility]::UrlEncode($query); $table_url = "https://{0}.table.core.windows.net/{1}?{2}" -f $this.StorageAccount, $tableName, $resource $headers = $this.GetHeader($tableName) $item = Invoke-RestMethod -Method Get -Uri $table_url -Headers $headers -ContentType "application/json" $azTableBugInfo = $item.value if ($azTableBugInfo -and $azTableBugInfo.count -gt 0) { $adoBugIds = @(); $adoBugIds += $azTableBugInfo.ADOBugId; $adoClosedBugResponse = $this.CloseBugsInBulk($adoBugIds); if($adoClosedBugResponse) { foreach ($row in $adoClosedBugResponse) { if($row.code -eq 200 ) { $id = ($row.body | ConvertFrom-Json).id $tableData = $azTableBugInfo | Where {$_.ADOBugId -eq $id} | Select PartitionKey, projectName, ADOScannerHashId #$isDeleted = $this.DeleteTableEntity($tableName, $tableData.partitionKey , $id); #if ($isDeleted -eq $true) { # $this.AddDataInTable($tableName, $tableData.partitionKey, $id, $tableData.projectName, "Y"); #} $isUpdated = $this.UpdateTableEntity($tableName, $tableData.partitionKey, $id, $tableData.projectName); #Adds ADOScannerHashId to response object $row.body=$row.body.TrimEnd("}") $row.body+=",`"ADOScannerHashId`":`"{0}`"" -f $tableData.ADOScannerHashId $row.body+="}" } } return $adoClosedBugResponse } } } catch { if (!$this.errorMsgDisplayed) { Write-Host "Could not update entry of closed bug in storage table." -ForegroundColor Red } return $null; } return $null; } hidden [string] GetTableName() { #return ($resourceNameToMakeTableName + "ADOBugInfo") -replace "[^a-zA-Z0-9]" return ($this.OrganizationName + "ADOBugInfo") -replace "[^a-zA-Z0-9]" } #function to close an active bug hidden [bool] CloseBug([string] $id, [string] $Project) { $url = "https://dev.azure.com/{0}/{1}/_apis/wit/workitems/{2}?api-version=6.0" -f $this.OrganizationName, $Project, $id #load the closed bug template $BugTemplate = [ConfigurationManager]::LoadServerConfigFile("TemplateForClosedBug.Json") $BugTemplate = $BugTemplate | ConvertTo-Json -Depth 10 $header = [WebRequestHelper]::GetAuthHeaderFromUriPatch($url) try { $responseObj = Invoke-RestMethod -Uri $url -Method Patch -ContentType "application/json-patch+json ; charset=utf-8" -Headers $header -Body $BugTemplate return $true; } catch { Write-Host $_ Write-Host "Could not close the bug." -ForegroundColor Red return $false } return $true; } #function to close an active bugs in bulk hidden [object] CloseBugsInBulk([string[]] $ids) { try { $closeBugTemplate = @(); foreach ($id in $ids) { $closeBugTemplate += [PSCustomObject] @{ method = 'PATCH'; uri = "/_apis/wit/workitems/$($id)?api-version=4.1"; headers = @{"Content-Type" = 'application/json-patch+json'}; body = @(@{op = "add"; "path"= "/fields/System.State"; "value"= "Closed"}; @{op = "add"; "path"= "/fields/Microsoft.VSTS.Common.ResolvedReason"; "value"= ""}) } } if ($closeBugTemplate.count -gt 0) { $body = $null; if ($closeBugTemplate.count -eq 1) { $body = "[$($closeBugTemplate | ConvertTo-Json -depth 10)]" } else { $body = $closeBugTemplate | ConvertTo-Json -depth 10 } $uri = 'https://{0}.visualstudio.com/_apis/wit/$batch?api-version=4.1' -f $this.OrganizationName $header = [WebRequestHelper]::GetAuthHeaderFromUriPatch($uri) $adoResult = Invoke-RestMethod -Uri $uri -Method Patch -ContentType "application/json" -Headers $header -Body $body if ($adoResult -and $adoResult.count -gt 0) { return $adoResult.value; } } return $false; } catch { Write-Host $_ Write-Host "Could not close the bug." -ForegroundColor Red return $false } } } # SIG # Begin signature block # MIIjiAYJKoZIhvcNAQcCoIIjeTCCI3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCArbfZryXk/cPz6 # 97JxDy3KJsSsmk/L3DoEcMr8LNrSF6CCDYEwggX/MIID56ADAgECAhMzAAAB32vw # LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn # s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw # PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS # yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG # 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh # EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH # tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS # 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp # TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok # t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4 # b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao # mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD # Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt # VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G # CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+ # Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82 # oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVXTCCFVkCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN # BglghkgBZQMEAgEFAKCBsDAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgIUJrVmxx # 1BNAn7QBJ8SZ1j5OXa07aaU7KICzKmjYy9IwRAYKKwYBBAGCNwIBDDE2MDSgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRyAGmh0dHBzOi8vd3d3Lm1pY3Jvc29mdC5jb20g # MA0GCSqGSIb3DQEBAQUABIIBABpaKDoFbM7IJ0wO3dV32I2AFDCmcSaJiwLfYRRA # pZtes24eutmDsW59+9TRFbImE/wfF0AcN9y1SwYQxbyqv1zl5i+ow7yBqwq/Kv4v # vwOQKFwApTx3wm7IZDfcemWR6sIdAkgNV5G6rqZpaLFnom/VjW7RIu/QuSJApK0b # n31B/F7H9cPGsi2cLQrbPO7HJ+7IFMXK7JpYZS+CibMsvPWMzle+QlLEtDa/kMTn # cRL+EHmODHLsTeLX3xpFSY1Ysr0F05xK5MUpfLv75yGgyPaLnEOcfYT5LkfFSCkc # Cagt3YHTt1bW1QVjAro/Fk6g6XsSnUGefyNBJv/knC5aAQ2hghLlMIIS4QYKKwYB # BAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFl # AwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkK # AwEwMTANBglghkgBZQMEAgEFAAQgtRGJDf0nen9fFIihyvdLvP7O6w6zDsZyMtEo # T3plJG0CBmD5hQXqXBgTMjAyMTA4MTMwOTA4MjQuODE5WjAEgAIB9KCB0KSBzTCB # yjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMc # TWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRT # UyBFU046RUFDRS1FMzE2LUM5MUQxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAAUzFTMHQ228/sgAAAAAB # TDANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAe # Fw0yMDExMTIxODI2MDBaFw0yMjAyMTExODI2MDBaMIHKMQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmlj # YSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpFQUNFLUUzMTYt # QzkxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMphYFHDrMe576NV7IEKD/jk37xP # iaTjee2zK3XP+qUJpBVMY2ICxaRRhy1Cnyf/5vWRpn33Bk9xbGegnpbkoL880bNp # SZ6uWcpzSgFBOdmNUrTBt96RWXaPY7ktUMBZEWviSf3yCV2IXgWYAQFuZ9ssQ9Yg # jpo1pvUrtaoUwAjiaM436UCU9fW1D+kcEH05m4hucWbE8JW+O9b3bletiv78n+fC # 6oKk6aSQRRFL4OJiovS+ib175G6pSf9wDRk9X3kO661OtCcrHZAfwe2MHXDP4eZf # GRksA/IvvrLFNcajI7It6Tx+onDyR5igRi+kCJoTG0YUGC1UMjCK05WtDrsCAwEA # AaOCARswggEXMB0GA1UdDgQWBBQBlh6nBApe5yeVQgGA9BBH3mb6fDAfBgNVHSME # GDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRw # Oi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQ # Q0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5o # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8y # MDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMI # MA0GCSqGSIb3DQEBCwUAA4IBAQBPBOSw99ZDrqiAYq9362Z3HYhBhoSXvMeICG9x # w7rlp8hAtmiSHPIAcM74xkfYZndBf1ZQ5unU5YmV+/PG/Qu7NX8ZKgkcsNW8UPAn # VbTpR+vNmf//kXdiDJP3b8U7nMzZ05peRKMV4vUOEYD6+ww8HNSSBEjRVfaESBLZ # 3opjPoxzayaop+WXU5ZWtloml3oLrnum1sicTVqw30mM2jY/wJJH/bK4bTRzzv7t # 7n18gB/+XC/YR/j2+tIuntj0xL0QUFG0XuBAL+6zLSCtJR36q0hP/77Zsk0txL95 # mNcrRfRQJy4xT5lkGIZXbAyEQg51BG5aomVO/1+05vrtz8prMIIGcTCCBFmgAwIB # AgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2Vy # dGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAx # MjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYw # JAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZI # hvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoA # goX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiE # VEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+B # VLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3w # V3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXo # eByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYw # ggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNo # WoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBW # BgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUH # AQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp # L2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGV # MIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWlj # cm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIw # NB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4A # dAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM # 9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0 # YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgP # F/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/62 # 5Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZq # kHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96 # LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5v # vfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiF # AR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduW # sqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV # 42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto2 # 29Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNh # IE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkVBQ0UtRTMxNi1D # OTFEMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEw # BwYFKw4DAhoDFQA9mVtOCSgTYnYdGM1jKASXGuD3oKCBgzCBgKR+MHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA5MBbEDAiGA8y # MDIxMDgxMzEwNDQzMloYDzIwMjEwODE0MTA0NDMyWjB3MD0GCisGAQQBhFkKBAEx # LzAtMAoCBQDkwFsQAgEAMAoCAQACAh2iAgH/MAcCAQACAhFoMAoCBQDkwayQAgEA # MDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAI # AgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAc+A2uBuCiOi8LZ3I6E8mVvq7J7sI # +vmZx5ULgm4qC2Euh769J7g47OtYXIJt34qoLUK88YhdZzcTUJjlMvEV/UEvKCRJ # GbvCUvPCfgOHZRHdlenMA4ZNsUv9dfEdElQFzo3hJqjoEGV9YoltB47GlMpdroE7 # kVzrjSj2bdV+s/sxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ # Q0EgMjAxMAITMwAAAUzFTMHQ228/sgAAAAABTDANBglghkgBZQMEAgEFAKCCAUow # GgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCMtWgo # 55l4/BZ8r1meFW1o6ueqGJhGMCVLwfyLtV3BgzCB+gYLKoZIhvcNAQkQAi8xgeow # gecwgeQwgb0EINvCpbu/UEsy0RBMIOH6TwsthlN90/tz2a8QYmfEr04lMIGYMIGA # pH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAFMxUzB0NtvP7IA # AAAAAUwwIgQglUo1TgNhgCBWILxGjteyXZMjyaBWrE2606RR49bEwOgwDQYJKoZI # hvcNAQELBQAEggEAx4lVVE7cpCPjH0DPVToLYC+Ial0GypvskfqvruYm2lnUuwSh # ob3bdwb4sSrYRcNOlHqXeqrApcwFd7ogR+e5yXpxu2kw73ZblBLxjxKl2xU8qhYS # EGN74lZYEzNBDFwyiuqsigLAUmsINYUAAOIv9rBnQEraqacakFiN/E7b+kpDShC2 # yP2ImGwpSu3cwbQ2RYoKGh0/ozVSHchrQpk7VdCqK86Of3C+mjk3W215FP4+eQXH # 2mzKDvQdT4Lu2S2aLgedRcodZsFuBQleoVoHxZuKJJjYS6j97Dk6AYSyNlIpO8z5 # miqNecQZ8pm1png1YtzL+ArI9vhbG0PbCaaB8A== # SIG # End signature block |