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; #Common storage account to fetch/store bug details. try { if ($env:CommonDataSA) { $this.StorageAccount = $env:CommonDataSA; } } catch { Write-Host "Could not find storage account. Storing bug details in default storage account [$($this.StorageAccount)]." -ForegroundColor Yellow } #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 # MIInzgYJKoZIhvcNAQcCoIInvzCCJ7sCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC3xigPtRIsex32 # GLedybWFm6wB6vPX35tC1y/G1h5MjaCCDYUwggYDMIID66ADAgECAhMzAAADri01 # UchTj1UdAAAAAAOuMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwODU5WhcNMjQxMTE0MTkwODU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQD0IPymNjfDEKg+YyE6SjDvJwKW1+pieqTjAY0CnOHZ1Nj5irGjNZPMlQ4HfxXG # yAVCZcEWE4x2sZgam872R1s0+TAelOtbqFmoW4suJHAYoTHhkznNVKpscm5fZ899 # QnReZv5WtWwbD8HAFXbPPStW2JKCqPcZ54Y6wbuWV9bKtKPImqbkMcTejTgEAj82 # 6GQc6/Th66Koka8cUIvz59e/IP04DGrh9wkq2jIFvQ8EDegw1B4KyJTIs76+hmpV # M5SwBZjRs3liOQrierkNVo11WuujB3kBf2CbPoP9MlOyyezqkMIbTRj4OHeKlamd # WaSFhwHLJRIQpfc8sLwOSIBBAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhx/vdKmXhwc4WiWXbsf0I53h8T8w # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwMTgzNjAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # AGrJYDUS7s8o0yNprGXRXuAnRcHKxSjFmW4wclcUTYsQZkhnbMwthWM6cAYb/h2W # 5GNKtlmj/y/CThe3y/o0EH2h+jwfU/9eJ0fK1ZO/2WD0xi777qU+a7l8KjMPdwjY # 0tk9bYEGEZfYPRHy1AGPQVuZlG4i5ymJDsMrcIcqV8pxzsw/yk/O4y/nlOjHz4oV # APU0br5t9tgD8E08GSDi3I6H57Ftod9w26h0MlQiOr10Xqhr5iPLS7SlQwj8HW37 # ybqsmjQpKhmWul6xiXSNGGm36GarHy4Q1egYlxhlUnk3ZKSr3QtWIo1GGL03hT57 # xzjL25fKiZQX/q+II8nuG5M0Qmjvl6Egltr4hZ3e3FQRzRHfLoNPq3ELpxbWdH8t # Nuj0j/x9Crnfwbki8n57mJKI5JVWRWTSLmbTcDDLkTZlJLg9V1BIJwXGY3i2kR9i # 5HsADL8YlW0gMWVSlKB1eiSlK6LmFi0rVH16dde+j5T/EaQtFz6qngN7d1lvO7uk # 6rtX+MLKG4LDRsQgBTi6sIYiKntMjoYFHMPvI/OMUip5ljtLitVbkFGfagSqmbxK # 7rJMhC8wiTzHanBg1Rrbff1niBbnFbbV4UDmYumjs1FIpFCazk6AADXxoKCo5TsO # zSHqr9gHgGYQC2hMyX9MGLIpowYCURx3L7kUiGbOiMwaMIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGZ8wghmbAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAOuLTVRyFOPVR0AAAAA # A64wDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEINg2 # Ubm0VO8ezQ2lzNiX+MjorF2K8Bkux6rq5izk5P0QMEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEAKsU2BB5GrcRFlX+60AvFIKqbWU6rWwzYqpcd # Q5bhhG38yTBkX7IdJSzbAJQJm7YBTYrzutXcazw9LjOKkc2GPr2kCxiMojJzDlpc # +vmMVNsQ08uYC4/ugrmvPy1QrHJuhHTESI9R4srV8WNnSX0v6PO2/dlk+chvI7D2 # f88CyISLhtMRaHuSZB/2VlSEIai/iTrDYQhtVweBcJV/L6U9+cJxsqf5vm3KYGVw # I6fymU9q1C3vuoHbBMQLIBvIbZNl9hKds863a3ErCVRHrcVjnJM/8F48w1VSkQ1/ # ZKdynaETsdRiNDVljlkXiMjtKxpc61K8M+9PgnH2yIOvmdo9uaGCFykwghclBgor # BgEEAYI3AwMBMYIXFTCCFxEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFZBgsqhkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCBLpvKF4lxOP71gy4QDR4dHnleejzjXk+sg # xafj5zJfLgIGZjOpsULaGBMyMDI0MDUxNTA5NTczMy45NzVaMASAAgH0oIHYpIHV # MIHSMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL # EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsT # HVRoYWxlcyBUU1MgRVNOOjA4NDItNEJFNi1DMjlBMSUwIwYDVQQDExxNaWNyb3Nv # ZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAHajtXJ # WgDREbEAAQAAAdowDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg # UENBIDIwMTAwHhcNMjMxMDEyMTkwNjU5WhcNMjUwMTEwMTkwNjU5WjCB0jELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9z # b2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMg # VFNTIEVTTjowODQyLTRCRTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJOQ # Bgh2tVFR1j8jQA4NDf8bcVrXSN080CNKPSQo7S57sCnPU0FKF47w2L6qHtwm4EnC # lF2cruXFp/l7PpMQg25E7X8xDmvxr8BBE6iASAPCfrTebuvAsZWcJYhy7prgCuBf # 7OidXpgsW1y8p6Vs7sD2aup/0uveYxeXlKtsPjMCplHkk0ba+HgLho0J68Kdji3D # M2K59wHy9xrtsYK+X9erbDGZ2mmX3765aS5Q7/ugDxMVgzyj80yJn6ULnknD9i4k # UQxVhqV1dc/DF6UBeuzfukkMed7trzUEZMRyla7qhvwUeQlgzCQhpZjz+zsQgpXl # PczvGd0iqr7lACwfVGog5plIzdExvt1TA8Jmef819aTKwH1IVEIwYLA6uvS8kRdA # 6RxvMcb//ulNjIuGceyykMAXEynVrLG9VvK4rfrCsGL3j30Lmidug+owrcCjQagY # mrGk1hBykXilo9YB8Qyy5Q1KhGuH65V3zFy8a0kwbKBRs8VR4HtoPYw9z1DdcJfZ # BO2dhzX3yAMipCGm6SmvmvavRsXhy805jiApDyN+s0/b7os2z8iRWGJk6M9uuT24 # 93gFV/9JLGg5YJJCJXI+yxkO/OXnZJsuGt0+zWLdHS4XIXBG17oPu5KsFfRTHREl # oR2dI6GwaaxIyDySHYOtvIydla7u4lfnfCjY/qKTAgMBAAGjggFJMIIBRTAdBgNV # HQ4EFgQUoXyNyVE9ZhOVizEUVwhNgL8PX0UwHwYDVR0jBBgwFoAUn6cVXQBeYl2D # 9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3Nv # ZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy # MDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1l # LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUB # Af8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQAD # ggIBALmDVdTtuI0jAEt41O2OM8CU237TGMyhrGr7FzKCEFaXxtoqk/IObQriq1ca # HVh2vyuQ24nz3TdOBv7rcs/qnPjOxnXFLyZPeaWLsNuARVmUViyVYXjXYB5DwzaW # ZgScY8GKL7yGjyWrh78WJUgh7rE1+5VD5h0/6rs9dBRqAzI9fhZz7spsjt8vnx50 # WExbBSSH7rfabHendpeqbTmW/RfcaT+GFIsT+g2ej7wRKIq/QhnsoF8mpFNPHV1q # /WK/rF/ChovkhJMDvlqtETWi97GolOSKamZC9bYgcPKfz28ed25WJy10VtQ9P5+C # /2dOfDaz1RmeOb27Kbegha0SfPcriTfORVvqPDSa3n9N7dhTY7+49I8evoad9hdZ # 8CfIOPftwt3xTX2RhMZJCVoFlabHcvfb84raFM6cz5EYk+x1aVEiXtgK6R0xn1wj # MXHf0AWlSjqRkzvSnRKzFsZwEl74VahlKVhI+Ci9RT9+6Gc0xWzJ7zQIUFE3Jiix # 5+7KL8ArHfBY9UFLz4snboJ7Qip3IADbkU4ZL0iQ8j8Ixra7aSYfToUefmct3dM6 # 9ff4Eeh2Kh9NsKiiph589Ap/xS1jESlrfjL/g/ZboaS5d9a2fA598mubDvLD5x5P # P37700vm/Y+PIhmp2fTvuS2sndeZBmyTqcUNHRNmCk+njV3nMIIHcTCCBVmgAwIB # AgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0 # IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1 # WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O # 1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZn # hUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t # 1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxq # D89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmP # frVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSW # rAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv # 231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zb # r17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYcten # IPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQc # xWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17a # j54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQAB # MCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQU # n6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEw # QTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9E # b2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQB # gjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/ # MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJ # oEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01p # Y1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYB # BQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9v # Q2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3h # LB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x # 5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74p # y27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1A # oL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbC # HcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB # 9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNt # yo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3 # rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcV # v7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A24 # 5oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lw # Y1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAtQwggI9AgEBMIIBAKGB2KSB1TCB # 0jELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMk # TWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1U # aGFsZXMgVFNTIEVTTjowODQyLTRCRTYtQzI5QTElMCMGA1UEAxMcTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAQqIfIYljHUbNoY0/ # wjhXRn/sSA2ggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN # BgkqhkiG9w0BAQUFAAIFAOnuocEwIhgPMjAyNDA1MTUxMDUxNDVaGA8yMDI0MDUx # NjEwNTE0NVowdDA6BgorBgEEAYRZCgQBMSwwKjAKAgUA6e6hwQIBADAHAgEAAgIQ # RzAHAgEAAgISdjAKAgUA6e/zQQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEE # AYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GB # AGo2c/W9A3RZMkyxuoIcP+bS3ZZ2E69eGKmaq8Vd2EEFfnKK926XeAqRd4utqNGG # pmdBFzkJrO8es6s8/6h89juy3d89ls5Fepi3dSBGi60rJdT9/lpC9nBcMrd4DP+x # Tp8KV5x+FjVEPlpM6pYEs99tCudypFZM3jND6MbfQKcpMYIEDTCCBAkCAQEwgZMw # fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd # TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAHajtXJWgDREbEAAQAA # AdowDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRAB # BDAvBgkqhkiG9w0BCQQxIgQg456JbR5NOoQAGjCppdeHb+ETUrmguZn2hbE/1l/K # 1o4wgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCAipaNpYsDvnqTe95Dj1C09 # 020I5ljibrW/ndICOxg9xjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD # QSAyMDEwAhMzAAAB2o7VyVoA0RGxAAEAAAHaMCIEIMQpV83bC/dOGotVp3Dt89cu # fr909qK3z2Gc4eOqBifOMA0GCSqGSIb3DQEBCwUABIICAHUnJ3hsfg2PDrXPi5MF # 9mY0975Ie0ihZkCf/gTSuOkbdS0v4NYgQBcJGR34jnPUaOzNO3WaP/WGx/USxHrg # zbUFnmye0josdyGr371VnVg1bAL3FKC5Xt4INRRhbSnlTfy0c4ETHH9L3lD1Yqt+ # shx3Ja+aLx7oyQx/+JBZMwOMXs02sO9R5zfHaWXiLcDx96JahKx1c5dKjYo6defG # 8LpljKSkxjKNNMovYAAmLAap138X/nHAwpxnr88JkWx98F8sPQ8UHyH5eq40ye3h # UuGe3lyBVPbDLul6qEyKO9MMFBqxq3H85AkLGqfb2RdknBxiNSNcd09+pZ4g7VGP # YU6MT0xMTmgxszB2xLFOtWMPmI7vGTOUXeh/TqwLWTJJrVO9XEd7IMk02enCSX+7 # zYXPYLGArVplIap0A7gNiuu1FzegkpKsMQGbjnDRVn/gFjQvpIguv8uAXzFJqY0v # WGA3otOeqt6UOMzgQ/VQWtKObyzjtOHgksiSG8XtLln6yXIx6AJvAw0HcBw/D5mX # MgoNfaJ8brn4E26G+fIZQkujkquJDLTrQumDCtyNJvl6OwXsqHm37/J3qWKnlEih # HPc8WN0bjSxXyq/03xHDu0+qoc+LSh2hrQCnX1iFFXkA4z2bxX7e5oIrFg5W26N+ # 5TbSvER4idGfD3zSgY+HMlYM # SIG # End signature block |