Private/Get-ADFSTkIssuanceTransformRules.ps1

function Get-ADFSTkIssuanceTransformRules
{
param (

    [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
    [string[]]$EntityCategories,
    [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=1)]
    [string]$EntityId,
    [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=2)]
    $RequestedAttribute,
    [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=3)]
    $RegistrationAuthority,
    [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=4)]
    $NameIDFormat
)

#Get All paths
if ([string]::IsNullOrEmpty($Global:ADFSTkPaths))
{
    $Global:ADFSTkPaths = Get-ADFSTKPaths
}

if ([string]::IsNullOrEmpty($Global:ADFSTkAllAttributes) -or $Global:ADFSTkAllAttributes.Count -eq 0)
{
    $Global:ADFSTkAllAttributes = Import-ADFSTkAllAttributes
}

if ([string]::IsNullOrEmpty($Global:ADFSTkAllTransformRules) -or $Global:ADFSTkAllTransformRules.Count -eq 0)
{
    $Global:ADFSTkAllTransformRules = Import-ADFSTkAllTransformRules

    if (Test-Path $Global:ADFSTkPaths.institutionLocalTransformRulesFile)
    {
        Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText rulesFederationLocalTransformRulesFoundFile)
        try {
            Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText rulesFederationLocalTransformRulesFile)
            . $Global:ADFSTkPaths.institutionLocalTransformRulesFile
    
            if (Test-Path function:Get-ADFSTkLocalTransformRules)
            {
                $localTransformRules = Get-ADFSTkLocalTransformRules
                Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText rulesFederationLocalTransformRulesFound -f $localTransformRules.Count)
    
                foreach ($transformRule in $localTransformRules.Keys)
                {
                    #Add or replace the standard Entoty Category with the federation one
                    if ($Global:ADFSTkAllTransformRules.ContainsKey($transformRule))
                    {
                        Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText rulesFederationLocalTransformRulesOverwrite -f $transformRule)
                    }
                    else
                    {
                        Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText rulesFederationLocalTransformRulesAdd -f $transformRule)
                    }
    
                    $Global:ADFSTkAllTransformRules.$transformRule = $localTransformRules.$transformRule
                }
            }
            else
            {
                Write-ADFSTkLog (Get-ADFSTkLanguageText rulesFederationLocalTransformRulesLoadFail) -EntryType Error
            }
        }
        catch
        {
            Write-ADFSTkLog (Get-ADFSTkLanguageText rulesFederationLocalTransformRulesLoadFail) -EntryType Error
        }
    }
    else {
        Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText rulesFederationLocalTransformRulesFileNotFound)
    }
}

$AllTransformRules = $Global:ADFSTkAllTransformRules #So we don't need to change anything in the Get-ADFSTkManualSPSettings files


$RequestedAttributes = @{}

if (![string]::IsNullOrEmpty($RequestedAttribute))
{
    $RequestedAttribute | % {
        $RequestedAttributes.($_.Name.trimEnd()) = $_.friendlyName
    }
}
else
{
    Write-ADFSTkLog (Get-ADFSTkLanguageText rulesNoRequestedAttributesDetected)
}

$IssuanceTransformRuleCategories = Import-ADFSTkIssuanceTransformRuleCategories -RequestedAttributes $RequestedAttributes

$adfstkConfig = Get-ADFSTkConfiguration

$federationDir = Join-Path $Global:ADFSTkPaths.federationDir $adfstkConfig.FederationConfig.Federation.FederationName
$fedEntityCategoryFileName = Join-Path $federationDir "$($adfstkConfig.FederationConfig.Federation.FederationName)_entityCategories.ps1"

if (Test-Path $fedEntityCategoryFileName)
{
    try {
        Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText rulesFederationEntityCategoryFile)
        . $fedEntityCategoryFileName

        if (Test-Path function:Import-ADFSTkIssuanceTransformRuleCategoriesFromFederation)
        {
            $IssuanceTransformRuleCategoriesFromFederation = Import-ADFSTkIssuanceTransformRuleCategoriesFromFederation -RequestedAttributes $RequestedAttributes
            Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText rulesFederationEntityCategoriesFound -f $IssuanceTransformRuleCategoriesFromFederation.Count)

            foreach ($entityCategory in $IssuanceTransformRuleCategoriesFromFederation.Keys)
            {
                #Add or replace the standard Entoty Category with the federation one
                if ($IssuanceTransformRuleCategories.ContainsKey($entityCategory))
                {
                    Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText rulesFederationEntityCategoryOverwrite -f $entityCategory)
                }
                else
                {
                    Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText rulesFederationEntityCategoryAdd -f $entityCategory)
                }

                $IssuanceTransformRuleCategories.$entityCategory = $IssuanceTransformRuleCategoriesFromFederation.$entityCategory
            }
        }
        else
        {
            #Write Verbose
        }
    }
    catch
    {
        Write-ADFSTkLog (Get-ADFSTkLanguageText rulesFederationEntityCategoryLoadFail) -EntryType Error
    }
}
else
{
    #Write Verbose
}


if ([string]::IsNullOrEmpty($Global:ADFSTkManualSPSettings))
{
    $Global:ADFSTkManualSPSettings = Get-ADFSTkManualSPSettings
}

### Transform Entity Categories

$TransformedEntityCategories = @()

$AttributesFromStore = @{}
$IssuanceTransformRules = [Ordered]@{}

$ManualSPTransformRules = $null

#Check version of get-ADFSTkLocalManualSpSettings and retrieve the transform rules
if ($EntityId -ne $null -and $Global:ADFSTkManualSPSettings.ContainsKey($EntityId))
{
    if ($Global:ADFSTkManualSPSettings.$EntityId -is [System.Collections.Hashtable] -and `
        $Global:ADFSTkManualSPSettings.$EntityId.ContainsKey('TransformRules'))
    {
        $ManualSPTransformRules = $Global:ADFSTkManualSPSettings.$EntityId.TransformRules
    }
    elseif ($Global:ADFSTkManualSPSettings.$EntityId -is [System.Collections.Specialized.OrderedDictionary])
    {
        $ManualSPTransformRules = $Global:ADFSTkManualSPSettings.$EntityId
    }
    else
    {
        #Shouldn't be here
    }
}

#Add manually added entity categories if any

if ($EntityId -ne $null -and `
    $Global:ADFSTkManualSPSettings.ContainsKey($EntityId) -and `
    $Global:ADFSTkManualSPSettings.$EntityId -is [System.Collections.Hashtable] -and `
    $Global:ADFSTkManualSPSettings.$EntityId.ContainsKey('EntityCategories'))
{
    $EntityCategories += $Global:ADFSTkManualSPSettings.$EntityId.EntityCategories
}


if ($EntityCategories -eq $null)
{
    $TransformedEntityCategories += "NoEntityCategory"
}
else
{
    foreach ($entityCategory in $IssuanceTransformRuleCategories.Keys)
    {
        if ($entityCategory -eq "http://www.swamid.se/category/research-and-education" -and $EntityCategories.Contains($entityCategory))
        {
            if ($EntityCategories.Contains("http://www.swamid.se/category/eu-adequate-protection") -or `
                $EntityCategories.Contains("http://www.swamid.se/category/nren-service") -or `
                $EntityCategories.Contains("http://www.swamid.se/category/hei-service"))
            {
                $TransformedEntityCategories += $entityCategory
            }
        }
        elseif ($EntityCategories.Contains($entityCategory)) 
        {
            $TransformedEntityCategories += $entityCategory
        }
    }

    if ($TransformedEntityCategories.Count -eq 0)
    {
        $TransformedEntityCategories += "NoEntityCategory"
    }

###

}

#region Add NameID to TransformRules
    if ([string]::IsNullOrEmpty($NameIDFormat))
    {
        $IssuanceTransformRules.'transient-id' = $Global:ADFSTkAllTransformRules.'transient-id'.Rule.Replace("[ReplaceWithSPNameQualifier]",$EntityId)
        foreach ($Attribute in $Global:ADFSTkAllTransformRules.'transient-id'.Attribute) { 
            $AttributesFromStore[$Attribute] = $Global:ADFSTkAllAttributes[$Attribute]
        }
    }
    elseif ($NameIDFormat.Contains('urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'))
    {
        $IssuanceTransformRules.'persistent-id' = $Global:ADFSTkAllTransformRules.'persistent-id'.Rule.Replace("[ReplaceWithSPNameQualifier]",$EntityId)
        foreach ($Attribute in $Global:ADFSTkAllTransformRules.'persistent-id'.Attribute) { 
            $AttributesFromStore[$Attribute] = $Global:ADFSTkAllAttributes[$Attribute]
        }
    }
    # elseif ($NameIDFormat.Contains('urn:oasis:names:tc:SAML:2.0:nameid-format:transient'))
    # {
    #
    # }
    else
    {
        $IssuanceTransformRules.'transient-id' = $Global:ADFSTkAllTransformRules.'transient-id'.Rule.Replace("[ReplaceWithSPNameQualifier]",$EntityId)
        foreach ($Attribute in $Global:ADFSTkAllTransformRules.'transient-id'.Attribute) { 
            $AttributesFromStore[$Attribute] = $Global:ADFSTkAllAttributes[$Attribute]
        }
    }
#endregion

#region Add TransformRules from categories
$TransformedEntityCategories | % { 

    if ($_ -ne $null -and $IssuanceTransformRuleCategories.ContainsKey($_))
    {
        foreach ($Rule in $IssuanceTransformRuleCategories[$_].Keys) { 
            if ($IssuanceTransformRuleCategories[$_][$Rule] -ne $null)
            {
                $IssuanceTransformRules[$Rule] = $IssuanceTransformRuleCategories[$_][$Rule].Rule.Replace("[ReplaceWithSPNameQualifier]",$EntityId)
                foreach ($Attribute in $IssuanceTransformRuleCategories[$_][$Rule].Attribute) { 
                    $AttributesFromStore[$Attribute] = $Global:ADFSTkAllAttributes[$Attribute]
                }
            }
        }
    }
}
#endregion

#AllSPs
if ($Global:ADFSTkManualSPSettings.ContainsKey('urn:adfstk:allsps'))
{
    foreach ($Rule in $Global:ADFSTkManualSPSettings['urn:adfstk:allsps'].TransformRules.Keys) { 
        if ($Global:ADFSTkManualSPSettings['urn:adfstk:allsps'].TransformRules[$Rule] -ne $null)
        {                
            $IssuanceTransformRules[$Rule] = $Global:ADFSTkManualSPSettings['urn:adfstk:allsps'].TransformRules[$Rule].Rule.Replace("[ReplaceWithSPNameQualifier]",$EntityId)
            foreach ($Attribute in $Global:ADFSTkManualSPSettings['urn:adfstk:allsps'].TransformRules[$Rule].Attribute) { 
                $AttributesFromStore[$Attribute] = $Global:ADFSTkAllAttributes[$Attribute]
            }
        }
    }
}

#AllEduSPs

if ($EntityId -ne $null)
{
    
    #First remove http:// or https://
    $entityDNS = $EntityId.ToLower().Replace('http://','').Replace('https://','')

    #Second get rid of all ending sub paths
    $entityDNS = $entityDNS -split '/' | select -First 1

    #Last fetch the last two words and join them with a .
    #$entityDNS = ($entityDNS -split '\.' | select -Last 2) -join '.'

    $settingsDNS = $null

    foreach($setting in $Global:ADFSTkManualSPSettings.Keys)
    {
        if ($setting.StartsWith('urn:adfstk:entityiddnsendswith:'))
        {
            $settingsDNS = $setting -split ':' | select -Last 1
        }
    }

    if ($entityDNS.EndsWith($settingsDNS) -and `
        $Global:ADFSTkManualSPSettings."urn:adfstk:entityiddnsendswith:$settingsDNS" -is [System.Collections.Hashtable] -and `
        $Global:ADFSTkManualSPSettings."urn:adfstk:entityiddnsendswith:$settingsDNS".ContainsKey('TransformRules'))
    {
        foreach ($Rule in $Global:ADFSTkManualSPSettings["urn:adfstk:entityiddnsendswith:$settingsDNS"].TransformRules.Keys) { 
            if ($Global:ADFSTkManualSPSettings["urn:adfstk:entityiddnsendswith:$settingsDNS"].TransformRules[$Rule] -ne $null)
            {                
                $IssuanceTransformRules[$Rule] = $Global:ADFSTkManualSPSettings["urn:adfstk:entityiddnsendswith:$settingsDNS"].TransformRules[$Rule].Rule.Replace("[ReplaceWithSPNameQualifier]",$EntityId)
                foreach ($Attribute in $Global:ADFSTkManualSPSettings["urn:adfstk:entityiddnsendswith:$settingsDNS"].TransformRules[$Rule].Attribute) { 
                    $AttributesFromStore[$Attribute] = $Global:ADFSTkAllAttributes[$Attribute]
                }
            }
        }
    }
}


#Manual SP
if ($ManualSPTransformRules -ne $null)
{
    foreach ($Rule in $ManualSPTransformRules.Keys) { 
        if ($ManualSPTransformRules[$Rule] -ne $null)
        {                
            $IssuanceTransformRules[$Rule] = $ManualSPTransformRules[$Rule].Rule.Replace("[ReplaceWithSPNameQualifier]",$EntityId)
            foreach ($Attribute in $ManualSPTransformRules[$Rule].Attribute) { 
                $AttributesFromStore[$Attribute] = $Global:ADFSTkAllAttributes[$Attribute]
            }
        }
    }
}

### This is a good place to remove attributes that shouldn't be sent outside a RegistrationAuthority
#$removeRules = @()
#foreach ($rule in $IssuanceTransformRules.Keys)
#{
# $attribute = $Settings.configuration.attributes.attribute | ? name -eq $rule
# if ($attribute -ne $null -and $attribute.allowedRegistrationAuthorities -ne $null)
# {
# $allowedRegistrationAuthorities = @()
# $allowedRegistrationAuthorities += $attribute.allowedRegistrationAuthorities.registrationAuthority
# if ($allowedRegistrationAuthorities.count -gt 0 -and !$allowedRegistrationAuthorities.contains($RegistrationAuthority))
# {
# $removeRules += $rule
# }
# }
#}
#
#$removeRules | % {$IssuanceTransformRules.Remove($_)}
#


$removeRules = @()
foreach ($attr in $AttributesFromStore.values)
{
    $attribute = $Settings.configuration.attributes.attribute | ? type -eq $attr.type
    if ($attribute -ne $null -and $attribute.allowedRegistrationAuthorities -ne $null)
    {
        $allowedRegistrationAuthorities = @()
        $allowedRegistrationAuthorities += $attribute.allowedRegistrationAuthorities.registrationAuthority
        if ($allowedRegistrationAuthorities.count -gt 0 -and !$allowedRegistrationAuthorities.contains($RegistrationAuthority))
        {
            $removeRules += $attr
        }
    }
}

$removeRules | % {
    
    $AttributesFromStore.Remove($_.type)
    foreach ($key in $Global:ADFSTkAllTransformRules.Keys) 
    {
        if ($Global:ADFSTkAllTransformRules.$key.Attribute -eq $_.type) 
        {
            $IssuanceTransformRules.Remove($key)
            break
        }
    }
}


###

#region Create Stores
if ($AttributesFromStore.Count -ne $null)
{

    $FirstRule = Get-ADFSTkStoreRule -Stores $Settings.configuration.storeConfig.stores.store `
                                     -AttributesFromStore $AttributesFromStore `
                                     -EntityId $EntityId 

    return  $FirstRule + $IssuanceTransformRules.Values
}
else
{
    return $IssuanceTransformRules.Values
}
#endregion
}
# SIG # Begin signature block
# MIIczwYJKoZIhvcNAQcCoIIcwDCCHLwCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUEZthh2PoSutt1UcnfmGIPVkV
# DQegghcwMIIEFDCCAvygAwIBAgILBAAAAAABL07hUtcwDQYJKoZIhvcNAQEFBQAw
# VzELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNV
# BAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0
# MTMxMDAwMDBaFw0yODAxMjgxMjAwMDBaMFIxCzAJBgNVBAYTAkJFMRkwFwYDVQQK
# ExBHbG9iYWxTaWduIG52LXNhMSgwJgYDVQQDEx9HbG9iYWxTaWduIFRpbWVzdGFt
# cGluZyBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlO9l
# +LVXn6BTDTQG6wkft0cYasvwW+T/J6U00feJGr+esc0SQW5m1IGghYtkWkYvmaCN
# d7HivFzdItdqZ9C76Mp03otPDbBS5ZBb60cO8eefnAuQZT4XljBFcm05oRc2yrmg
# jBtPCBn2gTGtYRakYua0QJ7D/PuV9vu1LpWBmODvxevYAll4d/eq41JrUJEpxfz3
# zZNl0mBhIvIG+zLdFlH6Dv2KMPAXCae78wSuq5DnbN96qfTvxGInX2+ZbTh0qhGL
# 2t/HFEzphbLswn1KJo/nVrqm4M+SU4B09APsaLJgvIQgAIMboe60dAXBKY5i0Eex
# +vBTzBj5Ljv5cH60JQIDAQABo4HlMIHiMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB
# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBRG2D7/3OO+/4Pm9IWbsN1q1hSpwTBHBgNV
# HSAEQDA+MDwGBFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFs
# c2lnbi5jb20vcmVwb3NpdG9yeS8wMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2Ny
# bC5nbG9iYWxzaWduLm5ldC9yb290LmNybDAfBgNVHSMEGDAWgBRge2YaRQ2XyolQ
# L30EzTSo//z9SzANBgkqhkiG9w0BAQUFAAOCAQEATl5WkB5GtNlJMfO7FzkoG8IW
# 3f1B3AkFBJtvsqKa1pkuQJkAVbXqP6UgdtOGNNQXzFU6x4Lu76i6vNgGnxVQ380W
# e1I6AtcZGv2v8Hhc4EvFGN86JB7arLipWAQCBzDbsBJe/jG+8ARI9PBw+DpeVoPP
# PfsNvPTF7ZedudTbpSeE4zibi6c1hkQgpDttpGoLoYP9KOva7yj2zIhd+wo7AKvg
# IeviLzVsD440RZfroveZMzV+y5qKu0VN5z+fwtmK+mWybsd+Zf/okuEsMaL3sCc2
# SI8mbzvuTXYfecPlf5Y1vC0OzAGwjn//UYCAp5LUs0RGZIyHTxZjBzFLY7Df8zCC
# BH0wggNloAMCAQICAxvnFTANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEh
# MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
# YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE0MDEwMTA3
# MDAwMFoXDTMxMDUzMDA3MDAwMFowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdB
# cml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNv
# bSwgSW5jLjExMC8GA1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRo
# b3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx
# +lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u
# 9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94L
# w7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd
# fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7
# S13MMuyFYkMlNAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsD
# PAnrSTFcaUaz4EcCAwEAAaOCARcwggETMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
# AQH/BAQDAgEGMB0GA1UdDgQWBBQ6moUHEGcotu/2vQVBbiDBlNoP3jAfBgNVHSME
# GDAWgBTSxLDSkdRMEXGzYcs9of7dqGrU4zA0BggrBgEFBQcBAQQoMCYwJAYIKwYB
# BQUHMAGGGGh0dHA6Ly9vY3NwLmdvZGFkZHkuY29tLzAyBgNVHR8EKzApMCegJaAj
# hiFodHRwOi8vY3JsLmdvZGFkZHkuY29tL2dkcm9vdC5jcmwwRgYDVR0gBD8wPTA7
# BgRVHSAAMDMwMQYIKwYBBQUHAgEWJWh0dHBzOi8vY2VydHMuZ29kYWRkeS5jb20v
# cmVwb3NpdG9yeS8wDQYJKoZIhvcNAQELBQADggEBAFkLU72ShhGnJHvtWzHPHR9s
# cMW4br5Ou/a+l1DhMH+6KFxilMLjfjP3+0J2hduVHIwiWHUJDIhlZzkKFgnFoDiX
# pMUjkz+0GKYBBkSR46dpJ7RaJX86tzLN3YT/KjgpM6TdZ7KF/qGIIBxQicjcKvZC
# AzdM5ojf1a8k8rHD38y17OCZXrdJVCA8lBgMxxxSGEmkbeGzWAvJ2OzZrhwyjihw
# DeL+pheehA+9V3CzWukfoIZTu+98/2kL4EjDt5MLyApUxKxdFGc3bMqlLzEIN6pu
# b4y8m+JXXSSBr5eXnIStbKw3TGbzYZERIOS+MJ96pCkJsOE0X2R3GEBR34wwpq8w
# ggSfMIIDh6ADAgECAhIRIdaZp2SXPvH4Qn7pGcxTQRQwDQYJKoZIhvcNAQEFBQAw
# UjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExKDAmBgNV
# BAMTH0dsb2JhbFNpZ24gVGltZXN0YW1waW5nIENBIC0gRzIwHhcNMTYwNTI0MDAw
# MDAwWhcNMjcwNjI0MDAwMDAwWjBgMQswCQYDVQQGEwJTRzEfMB0GA1UEChMWR01P
# IEdsb2JhbFNpZ24gUHRlIEx0ZDEwMC4GA1UEAxMnR2xvYmFsU2lnbiBUU0EgZm9y
# IE1TIEF1dGhlbnRpY29kZSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
# CgKCAQEAsBeuotO2BDBWHlgPse1VpNZUy9j2czrsXV6rJf02pfqEw2FAxUa1WVI7
# QqIuXxNiEKlb5nPWkiWxfSPjBrOHOg5D8NcAiVOiETFSKG5dQHI88gl3p0mSl9Rs
# kKB2p/243LOd8gdgLE9YmABr0xVU4Prd/4AsXximmP/Uq+yhRVmyLm9iXeDZGayL
# V5yoJivZF6UQ0kcIGnAsM4t/aIAqtaFda92NAgIpA6p8N7u7KU49U5OzpvqP0liT
# FUy5LauAo6Ml+6/3CGSwekQPXBDXX2E3qk5r09JTJZ2Cc/os+XKwqRk5KlD6qdA8
# OsroW+/1X1H0+QrZlzXeaoXmIwRCrwIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQD
# AgeAMEwGA1UdIARFMEMwQQYJKwYBBAGgMgEeMDQwMgYIKwYBBQUHAgEWJmh0dHBz
# Oi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMAkGA1UdEwQCMAAwFgYD
# VR0lAQH/BAwwCgYIKwYBBQUHAwgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny
# bC5nbG9iYWxzaWduLmNvbS9ncy9nc3RpbWVzdGFtcGluZ2cyLmNybDBUBggrBgEF
# BQcBAQRIMEYwRAYIKwYBBQUHMAKGOGh0dHA6Ly9zZWN1cmUuZ2xvYmFsc2lnbi5j
# b20vY2FjZXJ0L2dzdGltZXN0YW1waW5nZzIuY3J0MB0GA1UdDgQWBBTUooRKOFoY
# f7pPMFC9ndV6h9YJ9zAfBgNVHSMEGDAWgBRG2D7/3OO+/4Pm9IWbsN1q1hSpwTAN
# BgkqhkiG9w0BAQUFAAOCAQEAj6kakW0EpjcgDoOW3iPTa24fbt1kPWghIrX4RzZp
# juGlRcckoiK3KQnMVFquxrzNY46zPVBI5bTMrs2SjZ4oixNKEaq9o+/Tsjb8tKFy
# v22XY3mMRLxwL37zvN2CU6sa9uv6HJe8tjecpBwwvKu8LUc235IgA+hxxlj2dQWa
# NPALWVqCRDSqgOQvhPZHXZbJtsrKnbemuuRQ09Q3uLogDtDTkipbxFm7oW3bPM5E
# ncE4Kq3jjb3NCXcaEL5nCgI2ZIi5sxsm7ueeYMRGqLxhM2zPTrmcuWrwnzf+tT1P
# mtNN/94gjk6Xpv2fCbxNyhh2ybBNhVDygNIdBvVYBAexGDCCBNAwggO4oAMCAQIC
# AQcwDQYJKoZIhvcNAQELBQAwgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
# b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwg
# SW5jLjExMC8GA1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3Jp
# dHkgLSBHMjAeFw0xMTA1MDMwNzAwMDBaFw0zMTA1MDMwNzAwMDBaMIG0MQswCQYD
# VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEa
# MBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xLTArBgNVBAsTJGh0dHA6Ly9jZXJ0
# cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzEzMDEGA1UEAxMqR28gRGFkZHkgU2Vj
# dXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEF
# AAOCAQ8AMIIBCgKCAQEAueDLENSvdr3Uk2LrMGS4gQhswwTZYheOL/8+Zc+PzmLm
# PFIc2hZFS1WreGtjg2KQzg9pbJnIGhSLTMxFM+qI3J6jryv+gGGdeVfEzy70PzA8
# XUf8mha8wzeWQVGOEUtU+Ci+0Iy+8DA4HvOwJvhmR2Nt3nEmR484R1PRRh2049wA
# 6kWsvbxx2apvANvbzTA6eU9fTEf4He9bwsSdYDuxskOR2KQzTuqz1idPrSWKpcb0
# 1dCmrnQFZFeItURV1C0qOj74uL3pMgoClGTEFjpQ8Uqu53kzrwwgB3/o3wQ5wmkC
# bGNS+nfBG8h0h8i5kxhQVDVLaU68O9NJLh/cwdJS+wIDAQABo4IBGjCCARYwDwYD
# VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFEDCvSeOzDSD
# MKIz1/tss/C0LIDOMB8GA1UdIwQYMBaAFDqahQcQZyi27/a9BUFuIMGU2g/eMDQG
# CCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZ29kYWRkeS5j
# b20vMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwuZ29kYWRkeS5jb20vZ2Ry
# b290LWcyLmNybDBGBgNVHSAEPzA9MDsGBFUdIAAwMzAxBggrBgEFBQcCARYlaHR0
# cHM6Ly9jZXJ0cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsF
# AAOCAQEACH5skxDIOLiWqZBL/6FfTwTvbD6ciAbJUI+mc/dXMRu+vOQv2/i601vg
# tOfmeWIODKLXamNzMbX1qEikOwgtol2Q17R8JU8RVjDEtkSdeyyd5V7m7wxhqr/k
# KhvuhJ64g33BQ85EpxNwDZEf9MgTrYNg2dhyqHMkHrWsIg7KF4liWEQbq4klAQAP
# zcQbYttRtNMPUSqb9Lxz/HbONqTN2dgs6q6b9SqykNFNdRiKP4pBkCN9W0v+pANY
# m0ayw2Bgg/h9UEHOwqGQw7vvAi/SFVTuRBXZCq6nijPtsS12NibcBOuf92EfFdyH
# b+5GliitoSZ9CgmnLgSjjbz4vAQwATCCBRwwggQEoAMCAQICCDeMqUwECkf0MA0G
# CSqGSIb3DQEBCwUAMIG0MQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTET
# MBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4x
# LTArBgNVBAsTJGh0dHA6Ly9jZXJ0cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzEz
# MDEGA1UEAxMqR28gRGFkZHkgU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAt
# IEcyMB4XDTIwMDEwODExMjIyNFoXDTIxMDMwODE4NTgwMFowXjELMAkGA1UEBhMC
# Q0ExEDAOBgNVBAgTB09udGFyaW8xDzANBgNVBAcTBk90dGF3YTEVMBMGA1UEChMM
# Q0FOQVJJRSBJbmMuMRUwEwYDVQQDEwxDQU5BUklFIEluYy4wggEiMA0GCSqGSIb3
# DQEBAQUAA4IBDwAwggEKAoIBAQDZhfCjFqiTmN1uLoySixnwaOjf/ZAL9P6SvjlC
# aBA2mutoorEgnzUP8HnOIcvMRgEMPmpaZ8egM93Bmx9d41xoarsQpCN3DhYOo+b3
# fWnPucVtpxbul2OFePv63mw/uvr+dqkv4b/f3Tg+ilQbpsNonbvh9MKEFv8Pn9ko
# j0ySV+qxz34PxTVAe6g//pel3/3i9fqilCnIEcx4zg/+NKBeOWROSs4oXo3IvBjV
# runmz+YuieSr78TqIE6hD8JF2q1wKwfMB3+x7dEXZAus9WtIU/qITATtEfO9QAgr
# rYL4F1MLN+osSp8my5eCOjnLTQc47q574V3zQhsIHW7yBXLdAgMBAAGjggGFMIIB
# gTAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQE
# AwIHgDA1BgNVHR8ELjAsMCqgKKAmhiRodHRwOi8vY3JsLmdvZGFkZHkuY29tL2dk
# aWcyczUtNS5jcmwwXQYDVR0gBFYwVDBIBgtghkgBhv1tAQcXAjA5MDcGCCsGAQUF
# BwIBFitodHRwOi8vY2VydGlmaWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkv
# MAgGBmeBDAEEATB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
# Y3NwLmdvZGFkZHkuY29tLzBABggrBgEFBQcwAoY0aHR0cDovL2NlcnRpZmljYXRl
# cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5L2dkaWcyLmNydDAfBgNVHSMEGDAWgBRA
# wr0njsw0gzCiM9f7bLPwtCyAzjAdBgNVHQ4EFgQUUPnMg2nmYS8l7rmax3weVkrg
# z5AwDQYJKoZIhvcNAQELBQADggEBAIYabiARaY4KhO6oWgNHPOBjoHuqUH7NwRGN
# /ztYJznRBZbdD50smoK5GR0FvUZ8TXhYoZOazXe4NlFM4e6YcudU+EA/OF+sZHFB
# Wziz1VS6U3sS+cGyJcxvelSoid0q3W3i9/Zy6Nv2kk/DEJp49O47mPNovpL15yyk
# X3Vo26GwC9peo4s/cKMzthmgrcF2uLkT+LW44xKhaL7nBTGDMhjno+a3t00SWCId
# 7wzgQadIJ1QlFOKm0xgmuiW3LIqCG0apvaOfvWKTPKq68q+FbDPOO48oKrvw1c9K
# /m8gcnBLz6PX1REVIs5u3pvdOYBCz6uXyKxnt+Q5jDEK0NskLu0xggUJMIIFBQIB
# ATCBwTCBtDELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcT
# ClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMS0wKwYDVQQL
# EyRodHRwOi8vY2VydHMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeS8xMzAxBgNVBAMT
# KkdvIERhZGR5IFNlY3VyZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMgIIN4yp
# TAQKR/QwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJ
# KoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQB
# gjcCARUwIwYJKoZIhvcNAQkEMRYEFCDg/L2IkDAmCgjEOJAh8yyIYrrcMA0GCSqG
# SIb3DQEBAQUABIIBABX5qVZ7mWnEdGTF/D0P1t2AzCaHMyxlOQard/ZPqghnLqhP
# 09QuqJM2x0C1SMO+QMf3ken1pyXKg6ExDHCbQfsUuzanHL/GcPfwHoZK1SrsQovD
# glcgkCTzaBRZ2W3n0IFk/S9hVcbecaPyD0juB2JpA4mnO4m9fxwN1aUVvwJ5N110
# O6gnMOOR8/QCfV1qAR5J0i3dBMzd1GP2nSDFXWbyk782nRI3i68MO9+FC7H3t8GX
# 3366wm7VrWhmnILuOL7dhWiPNus5uxdD2pEJI3NdsqNQ0bFIHMqKbsWfmdEi8+bz
# UK9C4ChNCntxtQ7wFiMMdc3+Kz7Uk4MVpVcXDq2hggKiMIICngYJKoZIhvcNAQkG
# MYICjzCCAosCAQEwaDBSMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2ln
# biBudi1zYTEoMCYGA1UEAxMfR2xvYmFsU2lnbiBUaW1lc3RhbXBpbmcgQ0EgLSBH
# MgISESHWmadklz7x+EJ+6RnMU0EUMAkGBSsOAwIaBQCggf0wGAYJKoZIhvcNAQkD
# MQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjAxMTE2MjI0MTM2WjAjBgkq
# hkiG9w0BCQQxFgQUQ2zwPNgnkMWGoNVe12Njvjmp7UcwgZ0GCyqGSIb3DQEJEAIM
# MYGNMIGKMIGHMIGEBBRjuC+rYfWDkJaVBQsAJJxQKTPseTBsMFakVDBSMQswCQYD
# VQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEoMCYGA1UEAxMfR2xv
# YmFsU2lnbiBUaW1lc3RhbXBpbmcgQ0EgLSBHMgISESHWmadklz7x+EJ+6RnMU0EU
# MA0GCSqGSIb3DQEBAQUABIIBAAX7SQX/3CuWcaJgFiIk2bhLN868GR7CHAXf/+AY
# 5M380vRnxAJ0Q8wagbXHABYRMcQjdSIK2+IRMdIBIK2IDdRLBc2WxpSVHeY3wtKl
# R0MxoIIGL/HLV399/Ry4YuUz31eDZytI1q+kyUx12uW3bd/SGtrW7NZhUonkmq7Z
# +3iFvAEnzUrauM02BQl0Q1o6a4R/PHMf4gSbq/BBTOZBMn3q5RjATP1UReSZMSaT
# uEtpZGVw6iLQisgM7fTrtpPnI5ccV4o5G5gLW915M4M1GYXf3MUMqm7JDHYt0lY0
# 0JUwn+fN9Yx8/UGn6VYODkwJo2Q27Ctmlz0+A8fgM5S8czQ=
# SIG # End signature block