Framework/Core/STMapping/ServiceMappingCacheHelper.ps1
Set-StrictMode -Version Latest class ServiceMappingCacheHelper { hidden static [ServiceMappingCacheHelper] $ServiceMappingCacheHelperInstance; hidden [bool] $UseAzureStorageAccount; hidden [string] $OrganizationName; # Resource Caching Storage settings to cache mappings hidden [string] $CacheStorageName;# Storage account name for Caching hidden [string] $CacheStorageRG;# Storage resource group name for Caching hidden [string] $CacheTable;#Storage table to cache resource mapping hidden [object] $CacheStorageAccountCtx; hidden [string] $SharedKey hidden [object] $hmacsha ServiceMappingCacheHelper([string] $orgName) { $this.OrganizationName = $orgName; # Resources Caching Storage settings $this.CacheStorageName = $env:CacheStorageName; $this.CacheStorageRG = $env:CacheStorageRG; $this.CacheTable = $env:CacheTable; #get storage details if ($this.CacheStorageRG -and $this.CacheStorageName) { $keys = Get-AzStorageAccountKey -ResourceGroupName $this.CacheStorageRG -Name $this.CacheStorageName if ($null -ne $keys) { #storage context to cache resource mappings $CacheStorageContext = New-AzStorageContext -StorageAccountName $this.CacheStorageName -StorageAccountKey $keys[0].Value -Protocol Https $this.CacheStorageAccountCtx = $CacheStorageContext.Context; $this.SharedKey = $keys[0].Value; $this.hmacsha = New-Object System.Security.Cryptography.HMACSHA256 $this.hmacsha.key = [Convert]::FromBase64String($this.SharedKey) } } } #Return ServiceMappingCacheHelper instance hidden static [ServiceMappingCacheHelper] GetInstance([string] $orgName) { [ServiceMappingCacheHelper]::ServiceMappingCacheHelperInstance = [ServiceMappingCacheHelper]::new($orgName) return [ServiceMappingCacheHelper]::ServiceMappingCacheHelperInstance } #function to search for existing mapping based on hash id hidden [object] GetWorkItemByHashAzureTable([string] $resourceType, [string] $pipelineType, [string] $pipelineId,[string] $resourceId, [string] $projectID) { #get storage table data. $azTableMappingInfo = @(); try { $azTableMappingInfo += $this.GetTableEntity($projectID,$pipelineId,$pipelineType, $resourceId,$resourceType); if ($azTableMappingInfo -and $azTableMappingInfo.count -gt 0) { return $azTableMappingInfo; } } catch { Write-Host $_ Write-Host "Could not access storage account." -ForegroundColor Red } return $azTableMappingInfo; } hidden [bool] InsertMappingInfoInTable( [string] $orgName, [string] $projectID, [string] $pipelineID,[string] $pipelineName, [string] $serviceTreeID,[string] $pipelineLastModified,[string] $resourceID,[string] $resourceType,[string] $resourceName,[string] $pipelineType,[string] $mappingExpiration, [bool] $isIncrementalScan) { try { #Get table filterd by name. $storageTables = @(); $storageTables += Get-AzStorageTable -Context $this.CacheStorageAccountCtx | Select Name; #create table if table not found. if ( !$storageTables -or ($storageTables.count -eq 0) -or !($storageTables.Name -eq $this.CacheTable) ) { New-AzStorageTable $this.CacheTable -Context $this.CacheStorageAccountCtx; } $isDataAddedInTable = $this.AddDataInTable($orgName,$projectID,$pipelineID,$pipelineName,$serviceTreeID,$pipelineLastModified,$resourceID,$resourceType,$resourceName,$pipelineType, $mappingExpiration, $isIncrementalScan) return $isDataAddedInTable; } catch { return $false; } return $false } hidden [string] GenerateSearchQuery($projectID,$pipelineId,$pipelineType, $resourceId,$resourceType,$hash) { $query = 'PartitionKey eq ''{0}''' -f $hash; if($resourceType -eq "All") { return 'OrgName eq ''{0}'' and ProjectID eq ''{1}''' -f $this.OrganizationName, $projectID; } if($PSCmdlet.MyInvocation.BoundParameters["IncrementalScan"] -and -not ([string]::IsNullOrEmpty($resourceType))){ return 'OrgName eq ''{0}'' and ProjectID eq ''{1}'' and ResourceType eq ''{2}''' -f $this.OrganizationName, $projectID, $resourceType; } if($resourceType -eq "VariableGroup" -or $resourceType -eq "SecureFile" -and ![string]::IsNullOrEmpty($resourceId)) { return 'RowKey eq ''{0}''' -f $hash; } if($resourceType -eq "VariableGroup" -or $resourceType -eq "SecureFile" -and [string]::IsNullOrEmpty($resourceId)) { return 'OrgName eq ''{0}'' and PipelineType eq ''{1}''' -f $this.OrganizationName, $pipelineType; } return $query; } hidden [object] GetTableEntity($projectID,$pipelineId,$pipelineType, $resourceId,$resourceType) { try { $hash = $this.GetHashedTag($projectID, $pipelineID, $pipelineType,$resourceID,$resourceType) $query =$this.GenerateSearchQuery($projectID,$pipelineId,$pipelineType, $resourceId,$resourceType,$hash) $resource = '$filter='+[System.Web.HttpUtility]::UrlEncode($query); $table_url = "https://{0}.table.core.windows.net/{1}?{2}" -f $this.CacheStorageName, $this.CacheTable, $resource $headers = $this.GetHeader($this.CacheTable) $item = [WebRequestHelper]::InvokeWebRequest([Microsoft.PowerShell.Commands.WebRequestMethod]::Get,$table_url,$headers,"application/json; charset=UTF-8"); return $item; } catch { #Write-Host $_ Write-Host "Could not fetch the entry for partition key from table storage or the table was not found."; return $null } } hidden [bool] AddDataInTable([string] $orgName, [string] $projectID, [string] $pipelineID,[string] $pipelineName, [string] $serviceTreeID,[string] $pipelineLastModified,[string] $resourceID,[string] $resourceType,[string] $resourceName,[string] $pipelineType,[string] $mappingExpiration,[bool] $isIncrementalScan) { $partitionKey = $null; $rowKey = $null; if($isIncrementalScan){ $partitionKey = $this.GetHashedTag($projectID, "", "","",""); $rowKey = $this.GetHashedTag($projectID, "", "",$resourceID,$resourceType) } else{ $partitionKey = $this.GetHashedTag($projectID, $pipelineID, $pipelineType,"",""); $rowKey = $this.GetHashedTag($projectID, $pipelineID, $pipelineType,$resourceID,$resourceType) } try { #Add data in table. $entity = @{"PartitionKey" = $partitionKey; "RowKey" = $rowKey; "OrgName" = $orgName; "ProjectID" = $projectID; "PipelineID" = $pipelineID;"PipelineName" = $pipelineName;"ServiceTreeID" = $serviceTreeID;"PipelineLastModified" = $pipelineLastModified;"ResourceID" = $resourceID;"ResourceType" = $resourceType;"ResourceName" = $resourceName;"PipelineType" = $pipelineType; "MappingExpiration" = $MappingExpiration}; $table_url = "https://{0}.table.core.windows.net/{1}" -f $this.CacheStorageName, $this.CacheTable $headers = $this.GetHeader($this.CacheTable); $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([string] $orgName, [string] $projectID, [string] $pipelineID,[string] $pipelineName, [string] $serviceTreeID,[string] $pipelineLastModified,[string] $resourceID,[string] $resourceType,[string] $resourceName,[string] $pipelineType,[string] $mappingExpiration,[bool] $isIncrementalScan) { $partitionKey = $null; $rowKey = $null; if($isIncrementalScan){ $partitionKey = $this.GetHashedTag($projectID, "", "","",""); $rowKey = $this.GetHashedTag($projectID, "", "",$resourceID,$resourceType) } else{ $partitionKey = $this.GetHashedTag($projectID, $pipelineID, $pipelineType,"",""); $rowKey = $this.GetHashedTag($projectID, $pipelineID, $pipelineType,$resourceID,$resourceType) } try { #Update data in table. $tableName = $this.CacheTable; $entity = @{"OrgName" = $orgName; "ProjectID" = $projectID; "PipelineID" = $pipelineID;"PipelineName" = $pipelineName;"ServiceTreeID" = $serviceTreeID;"PipelineLastModified" = $pipelineLastModified;"ResourceID" = $resourceID;"ResourceType" = $resourceType;"ResourceName" = $resourceName;"PipelineType" = $pipelineType; "MappingExpiration" = $MappingExpiration}; $body = $entity | ConvertTo-Json $version = "2017-04-17" $resource = "$tableName(PartitionKey='$PartitionKey',RowKey='$Rowkey')" $table_url = "https://$($this.CacheStorageName).table.core.windows.net/$resource" $GMTTime = (Get-Date).ToUniversalTime().toString('R') $stringToSign = "$GMTTime`n/$($this.CacheStorageName)/$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.CacheStorageName + ":" + $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] DeleteDataInTable($projectID, $resourceID,$resourceType){ $partitionKey = $this.GetHashedTag($projectID, "", "","",""); $rowKey = $this.GetHashedTag($projectID, "", "",$resourceID,$resourceType); try{ $tableName = $this.CacheTable; $version = "2017-04-17" $resource = "$tableName(PartitionKey='$PartitionKey',RowKey='$Rowkey')" $table_url = "https://$($this.CacheStorageName).table.core.windows.net/$resource" $GMTTime = (Get-Date).ToUniversalTime().toString('R') $stringToSign = "$GMTTime`n/$($this.CacheStorageName)/$resource" $signature = $this.hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign)) $signature = [Convert]::ToBase64String($signature) $headers = @{ 'x-ms-date' = $GMTTime Authorization = "SharedKeyLite " + $this.CacheStorageName + ":" + $signature "x-ms-version" = $version 'If-Match' = "*" Accept = "application/json;odata=minimalmetadata" } Invoke-RestMethod -Method Delete -Uri $table_url -Headers $headers -ContentType "application/http" } catch{ return $false; } return $true; } hidden [object] GetHeader($tableName) { $version = "2017-07-29" $GMTTime = (Get-Date).ToUniversalTime().toString('R') $stringToSign = "$GMTTime`n/$($this.CacheStorageName)/$tableName" $signature = $this.hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign)) $signature = [Convert]::ToBase64String($signature) $headers = @{ 'x-ms-date' = $GMTTime Authorization = "SharedKeyLite " + $this.CacheStorageName + ":" + $signature "x-ms-version" = $version Accept = "application/json;odata=minimalmetadata" } return $headers } #function to compute hash and return the tag hidden [string] GetHashedTag([string] $projectID, [string] $pipelineID, [string] $pipelineType,[string] $resourceID,[string] $resourceType) { $stringToHash = "$projectID#$pipelineID#$pipelineType"; if(![string]::IsNullOrEmpty($resourceType)) { $stringToHash = "$projectID#$pipelineID##$pipelineType$resourceID#$resourceType"; } return $this.ComputeHashX($stringToHash); } hidden [string] ComputeHashX([string] $dataToHash) { return [Helpers]::ComputeHashShort($dataToHash, [Constants]::AutoBugLogTagLen) } static TelemetryLogging($eventName, $eventProps){ $telemetryClient = [Microsoft.ApplicationInsights.TelemetryClient]::new(); $telemetryClient.InstrumentationKey = $env:APPINSIGHTS_INSTRUMENTATIONKEY; $event = [Microsoft.ApplicationInsights.DataContracts.EventTelemetry]::new() $event.Name = $eventName if($null -ne $eventProps){ $eventProps.PSObject.Properties | ForEach-Object { try { $event.Properties[$_.Name] = $_.Value.ToString(); } catch { $_ # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } } } $telemetryClient.TrackEvent($event); } } # SIG # Begin signature block # MIInoAYJKoZIhvcNAQcCoIInkTCCJ40CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDDsRaxhmq652uX # NzrqiJpHZncGSOI8atCKfAffZo57eKCCDYUwggYDMIID66ADAgECAhMzAAACzfNk # v/jUTF1RAAAAAALNMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NjAyWhcNMjMwNTExMjA0NjAyWjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDrIzsY62MmKrzergm7Ucnu+DuSHdgzRZVCIGi9CalFrhwtiK+3FIDzlOYbs/zz # HwuLC3hir55wVgHoaC4liQwQ60wVyR17EZPa4BQ28C5ARlxqftdp3H8RrXWbVyvQ # aUnBQVZM73XDyGV1oUPZGHGWtgdqtBUd60VjnFPICSf8pnFiit6hvSxH5IVWI0iO # nfqdXYoPWUtVUMmVqW1yBX0NtbQlSHIU6hlPvo9/uqKvkjFUFA2LbC9AWQbJmH+1 # uM0l4nDSKfCqccvdI5l3zjEk9yUSUmh1IQhDFn+5SL2JmnCF0jZEZ4f5HE7ykDP+ # oiA3Q+fhKCseg+0aEHi+DRPZAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU0WymH4CP7s1+yQktEwbcLQuR9Zww # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ3MDUzMDAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # AE7LSuuNObCBWYuttxJAgilXJ92GpyV/fTiyXHZ/9LbzXs/MfKnPwRydlmA2ak0r # GWLDFh89zAWHFI8t9JLwpd/VRoVE3+WyzTIskdbBnHbf1yjo/+0tpHlnroFJdcDS # MIsH+T7z3ClY+6WnjSTetpg1Y/pLOLXZpZjYeXQiFwo9G5lzUcSd8YVQNPQAGICl # 2JRSaCNlzAdIFCF5PNKoXbJtEqDcPZ8oDrM9KdO7TqUE5VqeBe6DggY1sZYnQD+/ # LWlz5D0wCriNgGQ/TWWexMwwnEqlIwfkIcNFxo0QND/6Ya9DTAUykk2SKGSPt0kL # tHxNEn2GJvcNtfohVY/b0tuyF05eXE3cdtYZbeGoU1xQixPZAlTdtLmeFNly82uB # VbybAZ4Ut18F//UrugVQ9UUdK1uYmc+2SdRQQCccKwXGOuYgZ1ULW2u5PyfWxzo4 # BR++53OB/tZXQpz4OkgBZeqs9YaYLFfKRlQHVtmQghFHzB5v/WFonxDVlvPxy2go # a0u9Z+ZlIpvooZRvm6OtXxdAjMBcWBAsnBRr/Oj5s356EDdf2l/sLwLFYE61t+ME # iNYdy0pXL6gN3DxTVf2qjJxXFkFfjjTisndudHsguEMk8mEtnvwo9fOSKT6oRHhM # 9sZ4HTg/TTMjUljmN3mBYWAWI5ExdC1inuog0xrKmOWVMIIHejCCBWKgAwIBAgIK # 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/Xmfwb1tbWrJUnMTDXpQzTGCGXEwghltAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAALN82S/+NRMXVEAAAAA # As0wDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEINk+ # O3xFB6SjNmXGmM5BwSNAlyqyqaULUa9Xi3W/maLfMEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEAwhsauuXcaAToI86PyFwlvCDhnnQRgFX9831V # rOmJ+E7Sd0pnepbpyVkKdO5pu+oYS+zfuOK8tl1s8+Z58WCxlsDy5BX63EcoYvuS # UkcKbmjVs7bzihO5QdAC3cUUERK8wVonDNnRmDggT7Zjm/T8b5mM9+QwiuK+t0Ch # 6LvBPdu4JErGkrDOJ9htbeQF8ISHVtutOx1lGo3jQeIkEpcFJwIV8PFgsRDAu+BH # fFUrJsv17KTc7CLy9J3aR4FrAt7kmBPKEVe5blivIRj6W9AHI3pxlI+e1im/bV5E # d3DDnODIG3nGIlzaUXxIKBoRzyYIzVqwI1kFriERxYzaFWLsEqGCFvswghb3Bgor # BgEEAYI3AwMBMYIW5zCCFuMGCSqGSIb3DQEHAqCCFtQwghbQAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFQBgsqhkiG9w0BCRABBKCCAT8EggE7MIIBNwIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCAz9kHKPDyAB5HC7WIpzd1wtLEdHHvGpxCF # qC3SrO2sqwIGZDfpfeKlGBIyMDIzMDQxNzEwMzAwNi42OFowBIACAfSggdCkgc0w # gcoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsT # HE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBU # U1MgRVNOOjhBODItRTM0Ri05RERBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1T # dGFtcCBTZXJ2aWNloIIRUzCCBwwwggT0oAMCAQICEzMAAAHC+n2HDlRTRyQAAQAA # AcIwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw # HhcNMjIxMTA0MTkwMTI4WhcNMjQwMjAyMTkwMTI4WjCByjELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJp # Y2EgT3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046OEE4Mi1FMzRG # LTlEREExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC18Qm88o5IWfel62n3Byjb39Sg # mYMPIemaalGu5FVYEXfsHLSe+uzNJw5X8r4u8dZZYLhL1yZ7g/rcSY2HFM3+TYKA # +ci3+wN6nIAJKTJri6SpzWxPYj7RSh3TGPL0rb6MsfxQ1v28zfIf+8JidolJSqC2 # plSXLzamBhIHq0N5khhxCg6FMj4zUeFHGbG3xFoApmcOdeAt2SGchgMmtGRAGkiB # qG0TG1O46SZWnbLxgKgU9pSFQPYlPqE+IMPuoPsDvs8ukXMPZAWY17NPxoceEqxU # G4kws9dk7WTXiPT+TrwNka2zVgG0Z6Bc2TK+RdKAILG3dDxYXyVoFdsOeEdoMsGE # I4FplDyOpwVTHxklJdDyxu8SeZYVmaAz3cH0/8lMVMXqoFUUwN39XQ8FtFALZNy1 # kfht+/6PJa9k54XPnKW08tHFSoGO/gochomAGFTae0zDgfSGmbgHuzosvGROyMux # qOMIkjw+IqL+Y8pgRF2ZHK8Uvz9gD892qQjBZaDZOPm3K60YW19VH7oZtwJWGKOP # LuXui3Fr/BhVJfroujRqVpOGNz66iNXAfimwv4DWq9tYMH2zCgqVrbR5m5vDt/MK # kV7qqz74bTWyy3VJoDQYabO5AJ3ThR7V4fcMVENk+w35pa8DjnlCZ31kksZe6qcG # jgFfBXF1Zk2Pr5vg/wIDAQABo4IBNjCCATIwHQYDVR0OBBYEFEaxiHvpXjVpQJFc # T1a8P76wh8ZqMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1Ud # HwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3Js # L01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggr # BgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNv # bS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIw # MTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJ # KoZIhvcNAQELBQADggIBABF27d0KRwss99onetHUzsP2NYe+d59+SZe8Ugm2rEcZ # YzWioCH5urGkjsdnPYx42GHUKj4T0Bps6CP3hKnWx5fF1YhIn2VEZoABbMDzvdpM # Hf9KPC4apupC4C9TMEUI7jRQn1qelq+Smr/ScOotvtcjkf6eyaMXK7zKpfU8yadv # izV9tz8XfSKNoBLOon6nmuuBhbAOgyKEzlsXRjSuJeKHATt5NKFqT8TBzFGYbeH4 # 5P47Hwo4u4urAUWXWyJN5AKn5hK3gnW1ZdqmoYkOUJtivdHPz6vJNLwKhkBTS9Ic # I5ByrXZOHzWntCUdm/1xNEOFmDZNXKDwbHdfqaSk05dvnpBSiEjdKff1ZAnCMOfv # gnRpVgxqLyZjr9Y66sowoS5I2EKJ6LRMrry85juwfRcQFadFJtV595K0Oj3hQhRV # PB3YeYER9jyR+vKndGUD0DgW99S8McxoX0G29T+krp3UJ0obb1XRY3e5XN9gRMmh # GmMtgUarQy8rpBUya43GTdsJF+PVpxJZ57XhQaOCXFbC/I580l7enFw0U53weHKn # 13gCVAZUs2i1oW+imA8t4nBRPd2XlVoACEzC8gWarCx99DL3eaiuumtye/vivmDd # 6MLf01ikUSjL6qbMtbWMVrVpcTZdv8pnDbJrCqV1KnQe7hUSSMbEU1z4DO0hRbCZ # MIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsF # ADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UE # AxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcN # MjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFt # cCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzn # tHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3 # lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFE # yHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+ # jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4x # yDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBc # TyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9 # pSB9fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ # 8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pn # ol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYG # NRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cI # FRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEE # AYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E # 7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwr # BgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUF # BwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNV # HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYG # A1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3Js # L3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcB # AQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kv # Y2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUA # A4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2 # P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J # 6Gngugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfak # Vqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/AL # aoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtP # u4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5H # LcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEua # bvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvB # QUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb # /wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETR # kPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAsowggIzAgEB # MIH4oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQL # Ex1UaGFsZXMgVFNTIEVTTjo4QTgyLUUzNEYtOUREQTElMCMGA1UEAxMcTWljcm9z # b2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAynU3VUuE8y9Z # cShl+YXhqlmWdFSggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx # MDANBgkqhkiG9w0BAQUFAAIFAOfnBLUwIhgPMjAyMzA0MTcwNzM1MTdaGA8yMDIz # MDQxODA3MzUxN1owczA5BgorBgEEAYRZCgQBMSswKTAKAgUA5+cEtQIBADAGAgEA # AgEJMAcCAQACAhJIMAoCBQDn6FY1AgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisG # AQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQAD # gYEAHlU3t+DdFgZojirBjMpamnl8+9ky88nZzzBkW0omqZETMDgNxr0qll97eAbo # itiNJxuTjbyK+UPZRK9qY+qHi//+UAw5dYbOSGDjV4FkMN1bsFYzE4cc8+tC8bqV # HkwRAv7ULUK4U61kC+UnBeIfeYuZUHhQLNE/qnZqqZaQDMExggQNMIIECQIBATCB # kzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAcL6fYcOVFNHJAAB # AAABwjANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJ # EAEEMC8GCSqGSIb3DQEJBDEiBCDvVxKtrsafqsmpLry8NkxsRsoSVGBN3OdbmfDK # FPWBKTCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIMqTYFvH6bDFee69BeW3 # lLohFTlXuAXZmjvntvJhg1AWMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg # UENBIDIwMTACEzMAAAHC+n2HDlRTRyQAAQAAAcIwIgQg2zsP9L3cDp6JeFuhK79T # Vrr1HgEZyW54256vUoQVox8wDQYJKoZIhvcNAQELBQAEggIAfHkCe0w0fVZHJAQi # nP2XY+w6l1augLPd1a0zO1YLVd3UYZyovJ87vLPZLO1G+SijIwlmP/rP9huARhFk # A+0gbIOOl+fwaKmfv1tBlGNScuRs9epmDqZ2SS0l4Vg1CnxDU5gLibk8x5PNs67t # hklaD/P2q1eNzRjwHZYlSaw/vT6YphEph+8AIxhMvrsG5IuG7QqDamDdDfvO+xd4 # WX8qJNPm1dLxRMP1/erG1By+7XivzYQ1Bj0uEEIjHjLw7+PGjanhPL5nVjn3f9TC # Iha4AQJ3NNZJ2c2xa7nYp3e89bkGzRaroYmCzX/EqwxdFYOtuBy6vVJMemOe29B4 # XSTjmxXqSnXOsfBTW330lBuOq4yFz4tkLlREjoTgGdE1whS91QjXxezslFcA1Fpb # tRJ8OM1Ug8b70iqxjBTNWfp9VDsOgfJhBf+Dr013Pb/sgBx2fWMFtWN4UjdVo8TO # BNBF7md214EKAeT/4yHv/h6utM13Sd8W9a497fc37onnsKQ+LBAytnGKNDA8wYZb # eeLVhaupPhrEv2wNz2BoleZTTZjzWfUf4hMIL3RJ/Ae8gAc0FeTWvgeXHn/D5mv3 # u4XF71dlWsDLN+L9ZMl+tbzVWondc2nxfC/Ez/YhkzWCIkF6Gk3AhN3uTRj28SL9 # 2htwgS4RjBJiAGFztuS7Pt8zvu4= # SIG # End signature block |