serviceAccountModule/Tests/Test.ServiceAccount.ps1

#Define global constants
$script:Service_Account_Name = "newServiceAcct"

function New-ServiceAccount
{
    param(
        [Parameter(Mandatory=$true,Position=0)]
        [string]$username,
        
        [Parameter(Mandatory=$true,Position=1)]
        [string]$password
    )
    Process
    {
        ipmo ActiveDirectory
        $oldLocation = Get-Location
        Set-Location AD:
        
        $encryptedPassword = $password | ConvertTo-SecureString -asPlainText -Force

        try
        {
            $userObject = Get-ADUser $userName -ErrorAction SilentlyContinue
        }
        catch
        {
        }
        
        if ($userObject -eq $null)
        {
            New-ADUser -Name  $userName 
        }

        Get-ADUser $userName | Set-ADAccountPassword -Reset -NewPassword $encryptedPassword -PassThru 
        Get-ADUser $userName | Set-ADUser -PasswordNeverExpires $true -PassThru -Description "Password: $password" -Enabled $true
        
        Set-Location $oldLocation
    }
}

#####################################################################
####Helper functions related to rule parsing logic###################
#####################################################################

<#
.SYNOPSIS
    Class to encapsulate parsing of the ADFS Issuances/Auth rules.
#>


class AdfsRules
{
    [System.Collections.ArrayList] hidden $rules

    <#
    .SYNOPSIS
        Constructor
    #>

    AdfsRules([string]$rawRules) 
    {
        $rulesArray = $this.ParseRules($rawRules)
        $this.rules = New-Object "System.Collections.ArrayList"
        $this.rules.AddRange($rulesArray)
    }

    <#
    .SYNOPSIS
        Utility function to parse the rules and return them as a string[].
    #>

    [string[]] hidden ParseRules([string]$rawRules)
    {
        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : BEGIN"

        $allRules = @()
        $singleRule = [string]::Empty

        $rawRules.Split("`n") | %{
            
            $line = $_.ToString().Trim()

            if (-not ([string]::IsNullOrWhiteSpace($line)) ) 
            {
                $singleRule += $_ + "`n"

                if ($line.StartsWith("=>"))
                {
                    Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Parsed rule:`n$singleRule"
                    $allRules += $singleRule
                    $singleRule = [string]::Empty
                }
            }
        }

        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : END"

        return $allRules
    }

    [int] NumberOfRules()
    {
        return $this.rules.Count
    }

    <#
    .SYNOPSIS
        Finds the rule by name in the format: @RuleName = "$ruleName". Returns $null if not found.
    #>

    [string] FindByRuleName([string]$ruleName)
    {
        $ruleNameSearchString = '@RuleName = "' + $ruleName + '"'
        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Search string: $ruleNameSearchString"

        foreach ($rule in $this.rules)
        {
            if ($rule.Contains($ruleNameSearchString))
            {
                Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Found.`n$rule"
                return $rule
            }
        }

        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : NOT FOUND. Returning $null"
        return $null;
    }

    <#
    .SYNOPSIS
        Replaces the specified old rule with the new one. Returns $true if the old one was found and replaced; $false otherwise.
    #>

    [bool] ReplaceRule([string]$oldRule, [string]$newRule)
    {
        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Trying to replace old rule with new.`n Old Rule:`n$oldRule`n New Rule:`n$newRule"
        $idx = $this.FindIndexForRule($oldRule)

        if ($idx -ge 0)
        {
            Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Replacing old rule with new."
            $this.rules[$idx] = $newRule
            return $true
        }

        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Old rule is not found so NOT replacing it."
        return $false
    }

    <#
    .SYNOPSIS
        Removes the specified if found. Returns $true if found; $false otherwise.
    #>

    [bool] RemoveRule([string]$ruleToRemove)
    {
        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Trying to remove rule.`n Rule:`n$ruleToRemove"

        $idx = $this.FindIndexForRule($ruleToRemove)

        if ($idx -ge 0)
        {
            Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Removing rule at index: $idx."
            $this.rules.RemoveAt($idx)
            return $true
        }

        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Rule is not found so NOT removing it."
        return $false
    }

    <#
    .SYNOPSIS
        Helper function to find the index of the rule. Returns index if found; -1 otherwise.
    #>

    [int] FindIndexForRule([string]$ruleToFind)
    {
        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Trying to find rule.`n Rule:`n$ruleToFind"

        for ($i = 0; $i -lt $this.rules.Count; $i++)
        {
            $rule = $this.rules[$i]

            if ($rule.trim().Equals($ruleToFind.trim()))
            {
                Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : Found at index: $i."
                return $i
            }
        }

        Write-Verbose "$($PSCmdlet.MyInvocation.MyCommand) : NOT FOUND. Returning -1"
        return -1
    }
    
    <#
    .SYNOPSIS
        Returns all the rules as string.
    #>

    [string] ToString()
    {
        return [string]::Join("`n", $this.rules.ToArray())
    }
}

# Gets internal ADFS settings by extracting them Get-AdfsProperties
function Get-AdfsInternalSettings()
{
    $settings = Get-AdfsProperties
    $settingsType = $settings.GetType()
    $propInfo = $settingsType.GetProperty("ServiceSettingsData", [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic)
    $internalSettings = $propInfo.GetValue($settings, $null)
    
    return $internalSettings
}

function ValidateRules
{
    param
    (
        [parameter()]
        [switch]$CheckNotPresent
    )

    $Properties = Get-AdfsInternalSettings
    $AuthorizationPolicyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicy) 
    $AuthorizationPolicyReadOnlyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicyReadOnly)

    $SID = (New-Object system.security.principal.NtAccount($Service_Account_Name )).translate([system.security.principal.securityidentifier])
    $ServiceAccountRule = "@RuleName = `"Permit Service Account`"`nexists([Type == `"http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid`", Value == `"$SID`"])`n=> issue(Type = `"http://schemas.microsoft.com/authorization/claims/permit`", value = `"true`");`n`n"

    $AuthPolicyIndex = $AuthorizationPolicyRules.FindIndexForRule($ServiceAccountRule)
    $ReadOnlyIndex = $AuthorizationPolicyReadOnlyRules.FindIndexForRule($ServiceAccountRule)

    if($CheckNotPresent)
    {
        return ($AuthPolicyIndex -eq -1 -and $ReadOnlyIndex -eq -1)
    }
    return ($AuthPolicyIndex -ne -1 -and $ReadOnlyIndex -ne -1)

}


function Initialize()
{
    ipmo .\ServiceAccount.psm1
    #[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Simply a test password not used anywhere")]
    New-ServiceAccount -username $script:Service_Account_Name -password "Password"
}

Describe 'Basic functionality of adding and removing service account rule'{
    BeforeAll {
        Initialize
    }

    AfterAll {
        Remove-ADUser -Identity $script:Service_Account_Name
    }

    It "[00000]: Add-AdfsServiceAccountRule adds permit rule to ruleset"{
        Add-AdfsServiceAccountRule -ServiceAccount $script:Service_Account_Name
        ValidateRules | Should Be $true
    }

    It "[00000]: Add-AdfsServiceAccountRule fails if rule already exists"{
        $BeforeProperties = Get-AdfsInternalSettings
        $BeforeAuthorizationPolicyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicy) 
        $BeforeAuthorizationPolicyReadOnlyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicyReadOnly)

        Add-AdfsServiceAccountRule -ServiceAccount $script:Service_Account_Name

        $AfterProperties = Get-AdfsInternalSettings
        $AfterAuthorizationPolicyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicy) 
        $AfterAuthorizationPolicyReadOnlyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicyReadOnly)

        $AuthPolicyMatches = $BeforeAuthorizationPolicyRules.NumberOfRules() -eq $AfterAuthorizationPolicyRules.NumberOfRules()
        $ReadOnlyMatches = $BeforeAuthorizationPolicyReadOnlyRules.NumberOfRules() -eq $AfterAuthorizationPolicyReadOnlyRules.NumberOfRules()



        ($AuthPolicyMatches -eq $ReadOnlyMatches) | Should Be $true
    }

    It "[00000]: Remove-AdfsServiceAccountRule removes permit rule to ruleset"{
        Remove-AdfsServiceAccountRule -ServiceAccount $script:Service_Account_Name 
        ValidateRules -CheckNotPresent | Should Be $true
    }

    It "[00000]: Remove-AdfsServiceAccountRule does nothing if rule isn't present"{
        $BeforeProperties = Get-AdfsInternalSettings
        $BeforeAuthorizationPolicyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicy) 
        $BeforeAuthorizationPolicyReadOnlyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicyReadOnly)

        Remove-AdfsServiceAccountRule -ServiceAccount $script:Service_Account_Name

        $AfterProperties = Get-AdfsInternalSettings
        $AfterAuthorizationPolicyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicy) 
        $AfterAuthorizationPolicyReadOnlyRules = [AdfsRules]::new($Properties.PolicyStore.AuthorizationPolicyReadOnly)

        $AuthPolicyMatches = $BeforeAuthorizationPolicyRules.NumberOfRules() -eq $AfterAuthorizationPolicyRules.NumberOfRules()
        $ReadOnlyMatches = $BeforeAuthorizationPolicyReadOnlyRules.NumberOfRules() -eq $AfterAuthorizationPolicyReadOnlyRules.NumberOfRules()



        ($AuthPolicyMatches -eq $ReadOnlyMatches) | Should Be $true
    }

    It "[00000]: Add-AdfsServiceAccountRule adds permit rule to ruleset"{
        $ErrorThrown = $false
        try
        {
            Add-AdfsServiceAccountRule -ServiceAccount "fakeAccount"
        }
        catch
        {
            $ErrorThrown = $true
        }
        $ErrorThrown | Should Be $true
    }

}

# SIG # Begin signature block
# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC3n1Y9DcunGw7V
# 0CKrbUhKRsU1tx9P63a8jrrRT657pKCCDY0wggYLMIID86ADAgECAhMzAAABXXIc
# 0pGxWBxuAAAAAAFdMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMTkwNjA1MTczNDU2WhcNMjAwNjAzMTczNDU2WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDXRzz5YWPX74AM6Us0VX/pXRj2mfg5j9v6pTBCf9K2qMdqq8jyUq3MTwJ1v+Ol
# lkeMZHrkVt4EkuSxkHM+vx0crHXssknSA0itEBRAtj1gNYWzS+E9AfbRG2kKJHkV
# NbG/PoTemJRErrzfsyqBPlttr9OERi27B9pTs5lUOiVLOUKxnE6YkCqJZd5tHn2Q
# 17VErNpDGR+CbR4CujtLIrKuY3tN9CIu4n4Lzag7d3KMpcl/Tl2DFXW9pvQ9QrjT
# ecGxfne6RC8lucDMfMSr+GWzauN2iDgwW4UbrBzELzTxekwXF2tYhr4yttK55lQe
# WjIbPiGN1+9LisWk5Osm9l0JAgMBAAGjggGKMIIBhjArBgNVHSUEJDAiBgorBgEE
# AYI3TBMBBgorBgEEAYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUz+aU1rBrMBb9
# FzXNo21K673hqmAwUAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBP
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzMxMTArNDU1NTAzMB8G
# A1UdIwQYMBaAFEhuZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWG
# Q2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BD
# QTIwMTFfMjAxMS0wNy0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAC
# hkVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNp
# Z1BDQTIwMTFfMjAxMS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0B
# AQsFAAOCAgEAUnOcYhf4jLfyR6RspdPj0FmICyOVsx3MPpdxa1DigHv3PQhy3AjN
# mec7xoIwP+lSrO5qSIjh5wHpzNlcUBidOIFpdzHPGF1InmTHXczi82OXtGX1U09q
# OVUFy/P2TvAPyiSM+oQUmNT+UNKnCkSzk5Yxsa54J++bCU6ov3Z5+nCZ+Kb4oyhm
# BlFk8ogW0JePiIXERJirMErjmvg8pyM5UXlql+/lCD8CNN7kUTtckPnKmRAjFxMg
# EogwBE1WgqLuZmEQjts/I4Rk4jrzxoxCEWT1Y1H42MCP7id363Ay2rhyFEJchyu9
# rtR3n0R04lETRX3D6kgV+14Fc5kD49yaqDahAtD8NE8PMjt8HjoAkFf6XiW3R6+3
# D27Y2Iuxo6Q/nJKWPr0Dq7M8DBz8lXCF3jLInuZ4ByCA/UEjj4I48GHBbXEwUIqq
# a3s/29zC+RrikFtukY9Guwx1cnoJF4ekvNPP0AUM9NnXQobWfG22EZhC/NEz65R3
# WNNwHaDE5oQ2WVQfbL+e68Ecx2mSSKPRHtJwOCVKbypUsdYLfY30+n+sVRKd/kIv
# mVatChmB2o1esJS7h0Zw6UmofL+whAKEPjf2m0/oQqXkbJamP9gFn1isesc4uPfK
# RZ9jN9m82AI3Pf66KNyrsAXWrUdoeeMnPvW7zU+4Oomu7aX55TKc/HEwggd6MIIF
# YqADAgECAgphDpDSAAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9v
# dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0y
# NjA3MDgyMTA5MDlaMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZY
# IZ9CGypr6VpQqrgGOBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+
# lGAkbK+eSZzpaF7S35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDP
# s0S3XdjELgN1q2jzy23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJ
# KecNvqATd76UPe/74ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJ
# T4Qa8qEvWeSQOy2uM1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qc
# D60ZI4TL9LoDho33X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm
# 7GEfauEoSZ1fiOIlXdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/b
# wBWzvRvUVUvnOaEP6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKer
# jt/sW5+v/N2wZuLBl4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHt
# bcMojyyPQDdPweGFRInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70
# lrC8RqBsmNLg1oiMCwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYD
# VR0OBBYEFEhuZOVQBdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1
# AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaA
# FHItOgIxkEO5FAVO4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9j
# cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIw
# MTFfMjAxMV8wM18yMi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJo
# dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIw
# MTFfMjAxMV8wM18yMi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGD
# MD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2Rv
# Y3MvcHJpbWFyeWNwcy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8A
# cABvAGwAaQBjAHkAXwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQEL
# BQADggIBAGfyhqWY4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFt
# g/6+P+gKyju/R6mj82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/Wvj
# PgcuKZvmPRul1LUdd5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvt
# aPpoLpWgKj8qa1hJYx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+Z
# KJeYTQ49C/IIidYfwzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x
# 9Cf43iw6IGmYslmJaG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3bl
# QCplo8NdUmKGwx1jNpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8U
# vmFhtfDcxhsEvt9Bxw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGb
# pT9Fdx41xtKiop96eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNo
# deav+vyL6wuA6mk7r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uo
# zKRdwaGIm1dxVk5IRcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVWzCCFVcC
# AQEwgZUwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYG
# A1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAV1yHNKR
# sVgcbgAAAAABXTANBglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYB
# BAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0B
# CQQxIgQgjJwp2tvpEjQ3ffrwNJSNIpyxdU4gV1Ht6a5St0MNBK0wQgYKKwYBBAGC
# NwIBDDE0MDKgFIASAE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbTANBgkqhkiG9w0BAQEFAASCAQBr9hKyza/9TjE08Ptay2V65YZq
# y5nu+jejGu4a6dAnSP6DwclSuebANREXu/c/4JNxr0zJXEXBScvIbTqUw47/qkjR
# IEWQYxeCZX7l/V+W/LvSqgqzgDCzYCB8PxHv/IllSSPuXG3nOuojIxqeJSGyuDzA
# xjcEdqQUptRwIqt58EApv5jq3rFx0l/UjWlGcyIUpYZ9edaufnkXAKYY4i9ChnWH
# SxyttRF3d0CY7mjOSToLWELtpx47rmkS+4C6MBq9L1FzxUA2eb7+lePwmpP2xXnL
# lS3Prn1ZA1EX1AU1AoIvcFFMYfXwylPAj1erdbPd5NeeiXD+ghtBtsOV1apEoYIS
# 5TCCEuEGCisGAQQBgjcDAwExghLRMIISzQYJKoZIhvcNAQcCoIISvjCCEroCAQMx
# DzANBglghkgBZQMEAgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEB
# BgorBgEEAYRZCgMBMDEwDQYJYIZIAWUDBAIBBQAEIOejCyKdE/GnIUeXmrtP3kNc
# XWmtmGXPUDf4XthQ9jA+AgZdQg4CGI0YEzIwMTkwODIwMTgzNDE2Ljc1MlowBIAC
# AfSggdCkgc0wgcoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsT
# HVRoYWxlcyBUU1MgRVNOOkFFMkMtRTMyQi0xQUZDMSUwIwYDVQQDExxNaWNyb3Nv
# ZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIOPDCCBPEwggPZoAMCAQICEzMAAADt529y
# vU9VuNIAAAAAAO0wDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# UENBIDIwMTAwHhcNMTgxMDI0MjExNDE0WhcNMjAwMTEwMjExNDE0WjCByjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9z
# b2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046
# QUUyQy1FMzJCLTFBRkMxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
# cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD2xEpZbZ/UmTLv
# iEOzqh4fCWcfstmTccMK+HERQ0pAAYg3eCNWTbSZjtj/Tq8EbauxRnb0uU2oUaTs
# 6NhBwJ0GJKGGJf2ScQPfX2M3j1ThxUj5bOcUsG0r2E7yOhLCF7IdWlEvjOR9uVJS
# CQ+E3IFmEOnu+rgANcH/WcyZcmNjhyYZSYLzgie3gMTRpDfH/pxWOCM0ACbDZBts
# vee25jtyFJrsrzGo6qJ5DEhC+ZXBZEPIp1CgDazpuroTYIXR1jMyOa4U1LCIB3xN
# 49tcj2JhlhjoQdcPYF4B4YV+AMOR33JSthLMiy610yUGfKtkQnEiLu/cBLw4JC68
# z/BuS7rbAgMBAAGjggEbMIIBFzAdBgNVHQ4EFgQUwvQ7ncfS51XhCPESnwjeP55V
# fuswHwYDVR0jBBgwFoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBL
# oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljVGltU3RhUENBXzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr
# BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNU
# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAK
# BggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAQEAX5A86yzc9H5ybz61438E1yrE
# F6AE2t1NM/EpUqnbc9r9pCcLelXSWIc0vWcTkW2LotLRckwO+ehgDZM8lSxA294M
# +Wuqi/qLwU6uZ3VlU/VnngkOr8rS0xKivW0AZ0M58jqI8rlxDZpNB/V2jApEBjd+
# wXALSuo/oFXant/ql2Msa2L+1hcvSik1ei6/L/K2ZiKR3PSYOnHdTx6Wh+Hsk3ew
# aw/x+vfma8O+cedzGkjlHgufyDULsd2bLtPL6+NVZjnhPNnsSbnu7cf7MqWxc9Mm
# 7d+M4sSBSTuTExQosGwx6PC4T/YZrXFAi4U+9SVr1VT3OfM792+gRW51eL1vrDCC
# BnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29m
# dCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1
# NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw
# ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/
# aZRrdFQQ1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxh
# MFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhH
# hjKEHnRhZ5FfgVSxz5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tk
# iVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox
# 8NpOBpG2iAg16HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJN
# AgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIox
# kPNDe3xGG8UzaFqFbVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0P
# BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9
# lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQu
# Y29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3Js
# MFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3Nv
# ZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAG
# A1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAG
# CCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEA
# dABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXED
# PZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgr
# UYJEEvu5U4zM9GASinbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c
# 8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFw
# nzJKJ/1Vry/+tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFt
# w5yjojz6f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk
# 7Pf0v35jWSUPei45V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9d
# dJgiCGHasFAeb73x4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zG
# y9iCtHLNHfS4hQEegPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3
# yKxO2ii4sanblrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7c
# RDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wkn
# HNWzfjUeCLraNtvTX4/edIhJEqGCAs4wggI3AgEBMIH4oYHQpIHNMIHKMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3Nv
# ZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpB
# RTJDLUUzMkItMUFGQzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUAZieJEGxNFfeZgtCAQELn8/XVOMKggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOEGQX4wIhgPMjAxOTA4MjAxNzUzMDJaGA8yMDE5MDgyMTE3NTMwMlowdzA9Bgor
# BgEEAYRZCgQBMS8wLTAKAgUA4QZBfgIBADAKAgEAAgIehAIB/zAHAgEAAgIRuDAK
# AgUA4QeS/gIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBADpcXi7bcsB9JEvy
# smZ2UOA7teJHeNtL8tNHL12b+URGXVqSkUiWYT+5g7eOxda7yBtPFvR/o9CGlvQW
# pwvgNyVksnZLA69pd7GPbtdpWH6pOSq0A/OJ+POnoekr2nNeb4HqsoAyTMq98v7J
# cpLmyaecl8JSrQYwDFz2PyJ2XnHTMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAADt529yvU9VuNIAAAAAAO0wDQYJYIZIAWUD
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
# CQQxIgQgUQoGHw7PpWZNOTzdw2u8R/20AExi2ivIhB2F6wPYGMUwgfoGCyqGSIb3
# DQEJEAIvMYHqMIHnMIHkMIG9BCDRwej5cp3T8ozdHM1yAhkysHb0A5sZXQyaJHdR
# njCkvTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAA
# 7edvcr1PVbjSAAAAAADtMCIEIHm8FzRcbaOYtnruWXaKidpSUoLGV8tXIElL1IA/
# aK4tMA0GCSqGSIb3DQEBCwUABIIBACqI0wtNfdyLB4yFdR9d5aPvjSootVJ81xCA
# DyNJ+GrFeGx2iqFgXw2OKa2q/j5PCvoUuxS1g82dBSWCyQiN6OgYm8HQpD18foeO
# hVV5vuPSn0S5c27CAIUsmJgFxjWKJhwJPitmj+mBLRKpg86011PdXghv3uSpdo80
# ieTt6cXr3OmAUjvAiSjCo/GpmQdPzsjM6wWBFvdp2ekCeI1pfoZO525Elj4WPgAk
# sx4F3Z6tbhPIOL0R9yOVIb5ID5+RS2Os2UGVAzpIJ8V1KNG5HLT9b3Y6k8vegoa2
# ZMugiNHk06zL1AxQT/nP4PAg/pIqJisRjFVTjAV1iOSpW3jbSuk=
# SIG # End signature block