PowerFederatedDirectory.psm1

function Join-UriQuery { 
    <#
    .SYNOPSIS
    Provides ability to join two Url paths together including advanced querying
 
    .DESCRIPTION
    Provides ability to join two Url paths together including advanced querying which is useful for RestAPI/GraphApi calls
 
    .PARAMETER BaseUri
    Primary Url to merge
 
    .PARAMETER RelativeOrAbsoluteUri
    Additional path to merge with primary url (optional)
 
    .PARAMETER QueryParameter
    Parameters and their values in form of hashtable
 
    .EXAMPLE
    Join-UriQuery -BaseUri 'https://evotec.xyz/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' -QueryParameter @{
        page = 1
        per_page = 20
        search = 'SearchString'
    }
 
    .EXAMPLE
    Join-UriQuery -BaseUri 'https://evotec.xyz/wp-json/wp/v2/posts' -QueryParameter @{
        page = 1
        per_page = 20
        search = 'SearchString'
    }
 
    .EXAMPLE
    Join-UriQuery -BaseUri 'https://evotec.xyz' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts'
 
    .NOTES
    General notes
    #>

    [alias('Join-UrlQuery')]
    [CmdletBinding()]
    param ([parameter(Mandatory)][uri] $BaseUri,
        [parameter(Mandatory = $false)][uri] $RelativeOrAbsoluteUri,
        [Parameter()][System.Collections.IDictionary] $QueryParameter)
    if ($BaseUri -and $RelativeOrAbsoluteUri) { $Url = Join-Uri -BaseUri $BaseUri -RelativeOrAbsoluteUri $RelativeOrAbsoluteUri } else { $Url = $BaseUri }
    if ($QueryParameter) {
        $Collection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
        foreach ($key in $QueryParameter.Keys) { $Collection.Add($key, $QueryParameter.$key) }
    }
    $uriRequest = [System.UriBuilder] $Url
    if ($Collection) { $uriRequest.Query = $Collection.ToString() }
    return $uriRequest.Uri.AbsoluteUri
}
function Remove-EmptyValue { 
    [alias('Remove-EmptyValues')]
    [CmdletBinding()]
    param([alias('Splat', 'IDictionary')][Parameter(Mandatory)][System.Collections.IDictionary] $Hashtable,
        [string[]] $ExcludeParameter,
        [switch] $Recursive,
        [int] $Rerun,
        [switch] $DoNotRemoveNull,
        [switch] $DoNotRemoveEmpty,
        [switch] $DoNotRemoveEmptyArray,
        [switch] $DoNotRemoveEmptyDictionary)
    foreach ($Key in [string[]] $Hashtable.Keys) { if ($Key -notin $ExcludeParameter) { if ($Recursive) { if ($Hashtable[$Key] -is [System.Collections.IDictionary]) { if ($Hashtable[$Key].Count -eq 0) { if (-not $DoNotRemoveEmptyDictionary) { $Hashtable.Remove($Key) } } else { Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive } } else { if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } } } else { if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } } } }
    if ($Rerun) { for ($i = 0; $i -lt $Rerun; $i++) { Remove-EmptyValue -Hashtable $Hashtable -Recursive:$Recursive } }
}
function Join-Uri { 
    <#
    .SYNOPSIS
    Provides ability to join two Url paths together
 
    .DESCRIPTION
    Provides ability to join two Url paths together
 
    .PARAMETER BaseUri
    Primary Url to merge
 
    .PARAMETER RelativeOrAbsoluteUri
    Additional path to merge with primary url
 
    .EXAMPLE
    Join-Uri 'https://evotec.xyz/' '/wp-json/wp/v2/posts'
 
    .EXAMPLE
    Join-Uri 'https://evotec.xyz/' 'wp-json/wp/v2/posts'
 
    .EXAMPLE
    Join-Uri -BaseUri 'https://evotec.xyz/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts'
 
    .EXAMPLE
    Join-Uri -BaseUri 'https://evotec.xyz/test/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts'
 
    .NOTES
    General notes
    #>

    [alias('Join-Url')]
    [cmdletBinding()]
    param([parameter(Mandatory)][uri] $BaseUri,
        [parameter(Mandatory)][uri] $RelativeOrAbsoluteUri)
    return ($BaseUri.OriginalString.TrimEnd('/') + "/" + $RelativeOrAbsoluteUri.OriginalString.TrimStart('/'))
}
function Add-FederatedDirectoryUser {
    [alias('Add-FDUser')]
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Authorization,
        [string] $ExternalId,
        [parameter()][string] $DirectoryID,
        [parameter(Mandatory)][string] $UserName,
        [Alias('FirstName')] $FamilyName,
        [string] $GivenName,
        [parameter(Mandatory)][string] $DisplayName,
        [string] $NickName,
        [string] $ProfileUrl,
        [string] $EmailAddressWork,
        [string] $EmailAddressHome,
        [string] $StreetAddress,
        [string] $Locality,
        [string] $Region,
        [string] $PostalCode,
        [string] $Country,
        [string] $StreetAddressHome,
        [string] $PostalCodeHome,
        [string] $LocalityHome,
        [string] $RegionHome,
        [string] $CountryHome,
        [string] $PhoneNumberWork,
        [string] $PhoneNumberHome,
        [string] $PhoneNumberMobile,
        [string] $PhotoUrl,
        [string] $ThumbnailUrl,
        [string] $CompanyID,
        [string] $CompanyLogoUrl,
        [string] $CompanyThumbnailUrl,
        [string] $PreferredLanguage,
        [string] $Locale,
        [string] $TimeZone,
        [string] $Title,
        [string] $UserType,
        [string] $Password,
        [string] $ManagerID,
        [string] $ManagerUserName,
        [string] $ManagerReference,
        [string] $ManagerDisplayName,
        [switch] $Active,
        [string] $Department,
        [string] $EmployeeNumber,
        [string] $CostCenter,
        [string] $Division,
        [string] $Description,
        [ValidateSet('admin', 'user')][string] $Role = 'user',
        [alias('CustomAttribute01')][string] $Custom01,
        [alias('CustomAttribute02')][string] $Custom02,
        [alias('CustomAttribute03')][string] $Custom03,
        [switch] $Suppress
    )

    if (-not $Authorization) {
        if ($Script:AuthorizationCacheFD) {
            $Authorization = $Script:AuthorizationCacheFD[0]
        }
        if (-not $Authorization) {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw "No authorization found. Please run 'Connect-FederatedDirectory' first."
            } else {
                Write-Warning -Message "Add-FederatedDirectoryUser - No authorization found. Please run 'Connect-FederatedDirectory' first."
                return
            }
        }
    }
    if ($Authorization) {
        if ($ManagerUserName) {
            $ManagerID = (Get-FederatedDirectoryUser -Authorization $Authorization -UserName $ManagerUserName).Id
        }

        $Body = [ordered] @{
            schemas                                                      = @(
                "urn:ietf:params:scim:schemas:core:2.0:User"
                "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
                "urn:ietf:params:scim:schemas:extension:fd:2.0:User"
            )
            "externalId"                                                 = $ExternalId
            "userName"                                                   = $UserName
            "name"                                                       = [ordered] @{
                "familyName" = $FamilyName
                "givenName"  = $GivenName
            }
            "displayName"                                                = $DisplayName
            "nickName"                                                   = $NickName
            "profileUrl"                                                 = $ProfileUrl
            "emails"                                                     = @(
                if ($EmailAddressWork) {
                    @{
                        "value"   = $EmailAddressWork
                        "type"    = "work"
                        "primary" = $true
                    }
                }
                if ($EmailAddressHome) {
                    @{
                        "value" = $EmailAddressHome
                        "type"  = "home"
                    }
                }
            )
            "addresses"                                                  = @(
                if ($StreetAddress -or $Locality -or $Region -or $PostalCode -or $Country) {
                    @{
                        "streetAddress" = $StreetAddress
                        "locality"      = $Locality
                        "region"        = $Region
                        "postalCode"    = $PostalCode
                        "country"       = $Country
                        "type"          = "work"
                        "primary"       = $true
                    }
                }
                if ($StreetAddressHome -or $LocalityHome -or $RegionHome -or $PostalCodeHome -or $CountryHome) {
                    @{
                        "streetAddress" = $StreetAddressHome
                        "locality"      = $LocalityHome
                        "region"        = $RegionHome
                        "postalCode"    = $PostalCodeHome
                        "country"       = $CountryHome
                        "type"          = "home"
                    }
                }
            )
            "phoneNumbers"                                               = @(
                if ($PhoneNumberWork) {
                    @{
                        "value"   = $PhoneNumberWork
                        "type"    = "work"
                        "primary" = $true
                    }
                }
                if ($PhoneNumberHome) {
                    @{
                        "value" = $PhoneNumberHome
                        "type"  = "home"
                    }
                }
                if ($PhoneNumberMobile) {
                    @{
                        "value" = $PhoneNumberMobile
                        "type"  = "mobile"
                    }
                }
            )
            "photos"                                                     = @(
                if ($PhotoUrl) {
                    @{
                        "value" = $PhotoUrl
                        "type"  = "photo"
                    }
                }
                if ($ThumbnailUrl) {
                    @{
                        "value" = $ThumbnailUrl
                        "type"  = "thumbnail"
                    }
                }
            )
            "password"                                                   = $Password
            "preferredLanguage"                                          = $PreferredLanguage
            "locale"                                                     = $Locale
            "timeZone"                                                   = $TimeZone
            "userType"                                                   = $UserType
            "title"                                                      = $Title
            "active"                                                     = if ($PSBoundParameters.Keys -contains ('Active')) { $Active.IsPresent } else { $Null }
            "roles"                                                      = @(
                @{
                    "value"   = $Role
                    "display" = $Role
                }
            )
            "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" = [ordered] @{
                "organization"   = $Organization
                "department"     = $Department
                "employeeNumber" = $EmployeeNumber
                "costCenter"     = $CostCenter
                "division"       = $Division
                "manager"        = @{
                    "displayName" = $ManagerDisplayName
                    "value"       = $ManagerID
                    "`$ref"       = $ManagerReference # "../v2/Users/d09420a0-e97a-11e7-9faf-236ea7c81614"
                }
            }
            "urn:ietf:params:scim:schemas:extension:fd:2.0:User"         = [ordered] @{
                "description"  = $Description
                "companyId"    = $CompanyId
                "companyLogos" = @(
                    if ($CompanyLogoUrl) {
                        @{
                            "value" = $CompanyLogoUrl
                            "type"  = "logo"
                        }
                    }
                    if ($CompanyThumbnailUrl) {
                        @{
                            "value" = $CompanyThumbnailUrl
                            "type"  = "thumbnail"
                        }
                    }
                )
                'directoryId'  = $DirectoryID
                'custom01'     = $Custom01
                'custom02'     = $Custom02
                'custom03'     = $Custom03
            }
        }

        Try {
            Remove-EmptyValue -Hashtable $Body -Recursive -Rerun 2

            $invokeRestMethodSplat = [ordered] @{
                Method      = 'POST'
                Uri         = 'https://api.federated.directory/v2/Users'
                Headers     = [ordered]  @{
                    'Content-Type'  = 'application/json'
                    'Authorization' = $Authorization.Authorization
                    'Cache-Control' = 'no-cache'
                }
                Body        = $Body | ConvertTo-Json -Depth 10
                ErrorAction = 'Stop'
            }
            if ($DirectoryID) {
                $invokeRestMethodSplat['Headers']['directoryId'] = $DirectoryID
            }
            # for troubleshooting
            if ($VerbosePreference -eq 'Continue') {
                $Body | ConvertTo-Json -Depth 10 | Write-Verbose
            }
            $ReturnData = Invoke-RestMethod @invokeRestMethodSplat -Verbose:$false
            # don't return data as we trust it's been created
            if (-not $Suppress) {
                $ReturnData
            }
            # # for troubleshooting
            # if ($VerbosePreference -eq 'Continue') {
            # $invokeRestMethodSplat.Remove('body')
            # $invokeRestMethodSplat | ConvertTo-Json -Depth 10 | Write-Verbose
            # }
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                $ErrorDetails = $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue
                if ($ErrorDetails.Detail -like '*already exists*directory*') {
                    Write-Warning -Message "Add-FederatedDirectoryUser - $($ErrorDetails.Detail) [UserName: $UserName / DisplayName: $DisplayName]"
                } else {
                    Write-Warning -Message "Add-FederatedDirectoryUser - Error $($_.Exception.Message), $($ErrorDetails.Detail)"
                }
            }
        }
    } else {
        Write-Warning -Message 'Add-FederatedDirectoryUser - No authorization found. Please make sure to use Connect-FederatedDirectory first.'
    }
}
function Connect-FederatedDirectory {
    [alias('Connect-FD')]
    [cmdletbinding(DefaultParameterSetName = 'ClearText')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'ClearText')]
        [alias('ApplicationSecret', 'ApplicationKey')]
        [string] $Token,

        [Parameter(Mandatory, ParameterSetName = 'Encrypted')]
        [alias('ApplicationSecretEncrypted', 'ApplicationKeyEncrypted')]
        [string] $TokenEncrypted,

        [int] $ExpiresTimeout = 30,
        [switch] $ForceRefresh,
        [switch] $Suppress
    )
    if (-not $Script:AuthorizationCacheFD) {
        $Script:AuthorizationCacheFD = [ordered] @{}
    }

    if ($TokenEncrypted) {
        try {
            $ApplicationKeyTemp = $ClientSecretEncrypted | ConvertTo-SecureString -ErrorAction Stop
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                Write-Warning -Message "Connect-ILM - Error: $ErrorMessage"
                return
            }
        }
        $ApplicationKey = [System.Net.NetworkCredential]::new([string]::Empty, $ApplicationKeyTemp).Password
    } else {
        $ApplicationKey = $Token
    }

    $ShortKey = $ApplicationKey.Trim(15)

    $RestSplat = @{
        ErrorAction = 'Stop'
        Method      = 'POST'
        Body        = @{
            "grant_type" = "urn:ietf:params:oauth:grant-type:jwt-bearer"
            "assertion"  = $ApplicationKey
        }
        Uri         = 'https://api.federated.directory/v2/Login/Oauth2/Token'
    }

    if ($Script:AuthorizationCacheFD[$ShortKey] -and -not $ForceRefesh) {
        if ($Script:AuthorizationCacheFD[$ShortKey].ExpiresOn -gt [datetime]::UtcNow) {
            Write-Verbose "Connect-FederatedDirectory - Using cache for $ShortKey..."
            if (-not $Suppress) {
                return $Script:AuthorizationCacheFD[$ShortKey]
            }
        }
    }

    try {
        $Authorization = Invoke-RestMethod @RestSplat
        $Key = [ordered] @{
            'Authorization' = "$($Authorization.token_type) $($Authorization.access_token)"
            'Extended'      = $Authorization
            'Error'         = ''
            'ExpiresOn'     = ([datetime]::UtcNow).AddSeconds($Authorization.expires_in - $ExpiresTimeout)
            'Splat'         = [ordered] @{
                Token = $RestSplat['Body']['assertion']
            }
            'Platform'      = $Platform
        }
        $Script:AuthorizationCacheFD[$ShortKey] = $Key
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            Write-Warning -Message "Connect-ILM - Error: $ErrorMessage"
            $Key = [ordered] @{
                'Authorization' = $Null
                'Extended'      = $Null
                'Error'         = $ErrorMessage
                'ExpiresOn'     = $null
                'Splat'         = [ordered] @{
                    Token = $RestSplat['Body']['assertion']
                }
                'Platform'      = $Platform
            }
        }
    }
    if (-not $Suppress) {
        $Key
    }
}
function Get-FederatedDirectorySchema {
    [alias('Get-FDSchema')]
    [cmdletbinding()]
    param(   )

    $BaseUri = "https://api.federated.directory/v2/Schemas"

    Write-Verbose -Message "Get-FederatedDirectorySchema - Using query: $BaseUri"

    $Headers = @{
        'Content-Type' = 'application/json'
    }

    Try {
        $BatchObjects = Invoke-RestMethod -Method Get -Uri $BaseUri -Headers $Headers -ErrorAction Stop
        $BatchObjects.Resources
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            throw
        } else {
            $ErrorDetails = $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue
            Write-Warning -Message "Get-FederatedDirectorySchema - Error $($_.Exception.Message), $($ErrorDetails.Detail)"
        }
    }
}
function Get-FederatedDirectoryUser {
    [alias('Get-FDUser')]
    [cmdletbinding()]
    param(
        [System.Collections.IDictionary] $Authorization,
        [string] $Id,
        [string] $UserName,
        [string] $DirectoryID,
        [int] $MaxResults,
        [int] $StartIndex = 1,
        [int] $Count = 50,
        [string] $Filter,
        [ValidateSet(
            'id',
            'externalId',
            'userName',
            'givenName',
            'familyName',
            'displayName',
            'nickName',
            'profileUrl',
            'title',
            'userType',
            'emails',
            'phoneNumbers',
            'addresses',
            'preferredLanguage',
            'locale',
            'timezone',
            'active',
            'groups',
            'roles',
            'meta',
            'organization',
            'employeeNumber',
            'costCenter',
            'division',
            'department',
            'manager',
            'description',
            'directoryId',
            'companyId',
            'companyLogos',
            'custom01',
            'custom02',
            'custom03'
        )]
        [string] $SortBy,
        [ValidateSet('ascending', 'descending')][string] $SortOrder,
        [Alias('Property')]
        [ValidateSet(
            'id',
            'externalId',
            'userName',
            'givenName',
            'familyName',
            'displayName',
            'nickName',
            'profileUrl',
            'title',
            'userType',
            'emails',
            'phoneNumbers',
            'addresses',
            'preferredLanguage',
            'locale',
            'timezone',
            'active',
            'groups',
            'roles',
            'meta',
            'organization',
            'employeeNumber',
            'costCenter',
            'division',
            'department',
            'manager',
            'description',
            'directoryId',
            'companyId',
            'companyLogos',
            'custom01',
            'custom02',
            'custom03'
        )]
        [string[]] $Attributes
    )

    $ConvertAttributes = @{
        'id'                = 'id'
        'externalId'        = 'externalId'
        'userName'          = 'userName'
        'givenName'         = 'name.givenName'
        'familyName'        = 'name.familyName'
        'displayName'       = 'displayName'
        'nickName'          = 'nickName'
        'profileUrl'        = 'profileUrl'
        'title'             = 'title'
        'userType'          = 'userType'
        'emails'            = 'emails'
        'phoneNumbers'      = 'phoneNumbers'
        'addresses'         = 'addresses'
        'preferredLanguage' = 'preferredLanguage'
        'locale'            = 'locale'
        'timezone'          = 'timezone'
        'active'            = 'active'
        'groups'            = 'groups'
        'roles'             = 'roles'
        'meta'              = 'meta'
        'organization'      = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:organization'
        'employeeNumber'    = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber'
        'costCenter'        = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:costCenter'
        'division'          = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:division'
        'department'        = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department'
        'manager'           = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager'
        'description'       = 'urn:ietf:params:scim:schemas:extension:fd:2.0:User:description'
        'directoryId'       = 'urn:ietf:params:scim:schemas:extension:fd:2.0:User:directoryId'
        'companyId'         = 'urn:ietf:params:scim:schemas:extension:fd:2.0:User:companyId'
        'companyLogos'      = 'urn:ietf:params:scim:schemas:extension:fd:2.0:User:companyLogos'
        'custom01'          = 'urn:ietf:params:scim:schemas:extension:fd:2.0:User:custom01'
        'custom02'          = 'urn:ietf:params:scim:schemas:extension:fd:2.0:User:custom02'
        'custom03'          = 'urn:ietf:params:scim:schemas:extension:fd:2.0:User:custom03'
    }

    $AttributesConverted = foreach ($Attribute in $Attributes) {
        if ($ConvertAttributes[$Attribute]) {
            $ConvertAttributes[$Attribute]
        }
    }
    if ($SortBy) {
        $SortByConverted = $ConvertAttributes[$SortBy]
    }

    if (-not $Authorization) {
        if ($Script:AuthorizationCacheFD) {
            $Authorization = $Script:AuthorizationCacheFD[0]
        }
        if (-not $Authorization) {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw "No authorization found. Please run 'Connect-FederatedDirectory' first."
            } else {
                Write-Warning -Message "Get-FederatedDirectory - No authorization found. Please run 'Connect-FederatedDirectory' first."
                return
            }
        }
    }
    if ($Authorization) {
        if ($ID) {
            $BaseUri = "https://api.federated.directory/v2/Users/$ID"
        } else {
            $BaseUri = "https://api.federated.directory/v2/Users"
        }
        # Lets build up query
        $QueryParameter = [ordered] @{
            count      = if ($Count) { $Count } else { $null }
            startIndex = if ($StartIndex) { $StartIndex } else { $null }
            filter     = if ($UserName) {
                # keep in mind regardless of used operator it will always revert back to co as per API (weird)
                "userName co $UserName"
            } else {
                $Filter
            }
            sortBy     = $SortByConverted
            sortOrder  = $SortOrder
            attributes = $AttributesConverted -join ","
        }
        # lets remove empty values to remove whatever user hasn't requested
        Remove-EmptyValue -Hashtable $QueryParameter

        # Lets build our url
        $Uri = Join-UriQuery -BaseUri $BaseUri -QueryParameter $QueryParameter
        Write-Verbose -Message "Get-FederatedDirectoryUser - Using query: $Uri"

        $Headers = @{
            'Content-Type'  = 'application/json'
            'Authorization' = $Authorization.Authorization
            'directoryID'   = $DirectoryID
        }
        Remove-EmptyValue -Hashtable $Headers
        Try {
            $BatchObjects = Invoke-RestMethod -Method Get -Uri $Uri -Headers $Headers -ErrorAction Stop #{id}?attributes={attributes}'
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                $ErrorDetails = $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue
                if ($ErrorDetails.Detail -like '*already exists*directory*') {
                    Write-Warning -Message "Get-FederatedDirectoryUser - $($ErrorDetails.Detail) [UserName: $UserName / ID: $ID]"
                    return
                } else {
                    Write-Warning -Message "Get-FederatedDirectoryUser - Error $($_.Exception.Message), $($ErrorDetails.Detail)"
                    return
                }
            }
        }
        if ($BatchObjects.Resources) {
            Write-Verbose -Message "Get-FederatedDirectoryUser - Got $($BatchObjects.Resources.Count) users (StartIndex: $StartIndex, Count: $Count). Starting to process them."

            if ($MaxResults -gt 0 -and $BatchObjects.Resources.Count -ge $MaxResults) {
                # return users if amount of users available is more than we wanted
                $BatchObjects.Resources | Select-Object -First $MaxResults
                $LimitReached = $true
            } else {
                # return all users that were given in a batch
                $BatchObjects.Resources
            }
        } else {
            Write-Verbose "Get-FederatedDirectoryUser - No users found"
            return
        }
        if (-not $Count -and -not $StartIndex) {
            # paging is disabled, we don't do anything
        } elseif (-not $LimitReached -and $BatchObjects.TotalResults -gt $BatchObjects.StartIndex + $Count) {
            # lets get more users because there's more to get and user wanted more
            $MaxResults = $MaxResults - $BatchObjects.Resources.Count
            Write-Verbose "Get-FederatedDirectoryUser - Processing more pages (StartIndex: $StartIndex, Count: $Count)."
            $getFederatedDirectoryUserSplat = @{
                Authorization = $Authorization
                StartIndex    = $($BatchObjects.StartIndex + $Count)
                Count         = $Count
                MaxResults    = $MaxResults
                Filter        = $Filter
                SortBy        = $SortBy
                SortOrder     = $SortOrder
                Attributes    = $Attributes -join ","
                DirectoryID   = $DirectoryID
            }
            Remove-EmptyValue -Hashtable $getFederatedDirectoryUserSplat
            Get-FederatedDirectoryUser @getFederatedDirectoryUserSplat
        }
    } else {
        Write-Warning -Message 'Get-FederatedDirectoryUser - No authorization found. Please make sure to use Connect-FederatedDirectory first.'
    }
}

# $Script:AttributesList = @(
# 'id',
# 'externalId',
# 'userName',
# 'givenName',
# 'familyName',
# 'displayName',
# 'nickName',
# 'profileUrl',
# 'title',
# 'userType',
# 'emails',
# 'phoneNumbers',
# 'addresses',
# 'preferredLanguage',
# 'locale',
# 'timezone',
# 'active',
# 'groups',
# 'roles',
# 'meta',
# 'organization',
# 'employeeNumber',
# 'costCenter',
# 'division',
# 'department',
# 'manager',
# 'description',
# 'directoryId',
# 'companyId',
# 'companyLogos'
# 'custom01',
# 'custom02',
# 'custom03'
# )

# $Script:ScriptBlockAttributes = {
# param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
# $Script:AttributesList | Where-Object { $_ -like "*$wordToComplete*" }
# }

# Register-ArgumentCompleter -CommandName Get-FederatedDirectoryUser -ParameterName Attributes -ScriptBlock $Script:ScriptBlockAttributes
# Register-ArgumentCompleter -CommandName Get-FederatedDirectoryUser -ParameterName SortBy -ScriptBlock $Script:ScriptBlockAttributes
function Remove-FederatedDirectoryUser {
    <#
    .SYNOPSIS
    Remove a user from a federated directory.
 
    .DESCRIPTION
    Remove a user from a federated directory.
 
    .PARAMETER Authorization
    The authorization identity to use for the request from Connect-FederatedDirectory. If not specified, the default authorization identity will be used.
 
    .PARAMETER User
    The user to remove from the federated directory.
 
    .PARAMETER Id
    The id of the user to remove from the federated directory.
 
    .PARAMETER UserName
    The user name of the user to remove from the federated directory.
 
    .PARAMETER DirectoryID
    The id of the directory to remove the user from. If not specified, the default directory will be used.
 
    .EXAMPLE
     # remove specific user id
    Remove-FederatedDirectoryUser -Id '171a8cd0-2382-11ed-9dd1-b13400d703b6' -Verbose
 
    .EXAMPLE
    # get all ther users that contain name test user and delete them
    Remove-FederatedDirectoryUser -UserName 'testuser' -Verbose
 
    .EXAMPLE
     # get all ther users that contain name test user and delete them
    Get-FederatedDirectoryUser -UserName 'testuser' | Remove-FederatedDirectoryUser -Verbose
 
    .NOTES
    General notes
    #>

    [alias('Remove-FDUser')]
    [CmdletBinding(DefaultParameterSetName = 'Id')]
    param(
        [System.Collections.IDictionary] $Authorization,
        [parameter(Position = 0, ValueFromPipeline, Mandatory, ParameterSetName = 'User')][PSCustomObject[]] $User,
        [parameter(Mandatory, ParameterSetName = 'Id')][string[]] $Id,
        [parameter(Mandatory, ParameterSetName = 'UserName')][string[]] $UserName,
        [parameter()][string] $DirectoryID
    )
    Begin {
        if (-not $Authorization) {
            if ($Script:AuthorizationCacheFD) {
                $Authorization = $Script:AuthorizationCacheFD[0]
            }
            if (-not $Authorization) {
                if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                    throw "No authorization found. Please run 'Connect-FederatedDirectory' first."
                } else {
                    Write-Warning -Message "Get-FederatedDirectory - No authorization found. Please run 'Connect-FederatedDirectory' first."
                    return
                }
            }
        }
    }
    Process {
        if ($Authorization) {
            if ($Id) {
                $RemoveID = $Id
            } elseif ($User) {
                $RemoveID = $User.Id
            } elseif ($UserName) {
                $RemoveID = Foreach ($U in $UserName) {
                    (Get-FederatedDirectoryUser -Authorization $Authorization -UserName $U).Id
                }
            } else {
                return
            }
            foreach ($I in $RemoveID) {
                Try {
                    $invokeRestMethodSplat = [ordered] @{
                        Method      = 'DELETE'
                        Uri         = "https://api.federated.directory/v2/Users/$I"
                        Headers     = [ordered]  @{
                            'Content-Type'  = 'application/json'
                            'Authorization' = $Authorization.Authorization
                            'Cache-Control' = 'no-cache'
                            'directoryId'   = $DirectoryID
                        }
                        ErrorAction = 'Stop'
                    }
                    Remove-EmptyValue -Hashtable $invokeRestMethodSplat

                    if ($VerbosePreference -eq 'Continue') {
                        $invokeRestMethodSplat | ConvertTo-Json -Depth 10 | Write-Verbose
                    }

                    $ReturnData = Invoke-RestMethod @invokeRestMethodSplat
                    $ReturnData
                } catch {
                    $ErrorDetails = $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue
                    if ($ErrorDetails.Detail -like "*not found*") {
                        Write-Warning -Message "Remove-FederatedDirectoryUser - $($ErrorDetails.Detail)."
                    } else {
                        Write-Warning -Message "Remove-FederatedDirectoryUser - Error $($_.Exception.Message), $($ErrorDetails.Detail)"
                    }
                }
            }
        } else {
            Write-Warning -Message 'Remove-FederatedDirectoryUser - No authorization found. Please make sure to use Connect-FederatedDirectory first.'
        }
    }
}
function Set-FederatedDirectoryUser {
    [alias('Set-FDUser')]
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Authorization,
        [string] $Id,
        [string] $ExternalId,
        [parameter()][string] $DirectoryID,
        [parameter()][string] $UserName,
        [Alias('FirstName')] $FamilyName,
        [string] $GivenName,
        [parameter()][string] $DisplayName,
        [string] $NickName,
        [string] $ProfileUrl,
        [string] $EmailAddressWork,
        [string] $EmailAddressHome,
        [string] $StreetAddress,
        [string] $Locality,
        [string] $Region,
        [string] $PostalCode,
        [string] $Country,
        [string] $StreetAddressHome,
        [string] $PostalCodeHome,
        [string] $LocalityHome,
        [string] $RegionHome,
        [string] $CountryHome,
        [string] $PhoneNumberWork,
        [string] $PhoneNumberHome,
        [string] $PhoneNumberMobile,
        [string] $PhotoUrl,
        [string] $ThumbnailUrl,
        [string] $CompanyID,
        [string] $CompanyLogoUrl,
        [string] $CompanyThumbnailUrl,
        [string] $PreferredLanguage,
        [string] $Locale,
        [string] $TimeZone,
        [string] $Title,
        [string] $UserType,
        [string] $Password,
        [string] $ManagerID,
        [string] $ManagerUserName,
        [string] $ManagerReference,
        [string] $ManagerDisplayName,
        [bool] $Active,
        [string] $Department,
        [string] $EmployeeNumber,
        [string] $CostCenter,
        [string] $Division,
        [string] $Description,
        [ValidateSet('admin', 'user')][string] $Role,
        [alias('CustomAttribute01')][string] $Custom01,
        [alias('CustomAttribute02')][string] $Custom02,
        [alias('CustomAttribute03')][string] $Custom03,
        [switch] $Suppress,
        [ValidateSet('Overwrite', 'Update')][string] $Action = 'Update',
        [System.Collections.IDictionary] $ActionPerProperty = @{}
    )
    if (-not $Authorization) {
        if ($Script:AuthorizationCacheFD) {
            $Authorization = $Script:AuthorizationCacheFD[0]
        }
        if (-not $Authorization) {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw "No authorization found. Please run 'Connect-FederatedDirectory' first."
            } else {
                Write-Warning -Message "Set-FederatedDirectoryUser - No authorization found. Please run 'Connect-FederatedDirectory' first."
                return
            }
        }
    }
    if ($Authorization) {
        if ($Action -eq 'Update') {
            $TranslatePath = @{
                UserName            = "userName"
                ExternalId          = "externalId"
                FamilyName          = "name.familyName"
                Password            = "password" # not used yet
                # not used yet
                Role                = "roles.value" # "admin" or "user"
                GivenName           = 'name.givenName'
                DisplayName         = 'displayName'
                NickName            = 'nickName'
                ProfileUrl          = 'profileUrl'
                EmailAddressWork    = 'emails[type eq "work"].value'
                EmailAddressHome    = 'emails[type eq "home"].value'
                StreetAddress       = 'addresses[type eq "work"].streetAddress'
                Locality            = 'addresses[type eq "work"].locality'
                Region              = 'addresses[type eq "work"].region'
                PostalCode          = 'addresses[type eq "work"].postalCode'
                Country             = 'addresses[type eq "work"].country'
                StreetAddressHome   = 'addresses[type eq "home"].streetAddress'
                PostalCodeHome      = 'addresses[type eq "home"].postalCode'
                LocalityHome        = 'addresses[type eq "home"].locality'
                RegionHome          = 'addresses[type eq "home"].region'
                CountryHome         = 'addresses[type eq "home"].country'
                PhoneNumberWork     = 'phoneNumbers[type eq "work"].value'
                PhoneNumberHome     = 'phoneNumbers[type eq "home"].value'
                PhoneNumberMobile   = 'phoneNumbers[type eq "mobile"].value'
                PhotoUrl            = 'photos[type eq "photo"].value'
                ThumbnailUrl        = 'photos[type eq "thumbnail"].value'
                Title               = 'title'
                UserType            = 'userType'
                Active              = 'active'
                TimeZone            = 'timezone'
                Locale              = 'locale'
                PreferredLanguage   = 'preferredLanguage'
                EmployeeNumber      = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber'
                CostCenter          = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:costCenter'
                Division            = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:division'
                Department          = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department'
                Organization        = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:organization'
                ManagerID           = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.value'
                ManagerUserName     = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.value'
                ManagerReference    = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.$ref'
                ManagerDisplayName  = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.displayName'
                Description         = "urn:ietf:params:scim:schemas:extension:fd:2.0:User:description"
                Custom01            = "urn:ietf:params:scim:schemas:extension:fd:2.0:User:custom01"
                Custom02            = "urn:ietf:params:scim:schemas:extension:fd:2.0:User:custom02"
                Custom03            = "urn:ietf:params:scim:schemas:extension:fd:2.0:User:custom03"
                CompanyID           = "urn:ietf:params:scim:schemas:extension:fd:2.0:User:companyId"
                CompanyLogoUrl      = 'urn:ietf:params:scim:schemas:extension:fd:2.0:User:companyLogos[type eq "logo"].value'
                CompanyThumbnailUrl = 'urn:ietf:params:scim:schemas:extension:fd:2.0:User:companyLogos[type eq "thumbnail"].value'
            }

            if ($ManagerUserName) {
                $ManagerID = (Get-FederatedDirectoryUser -Authorization $Authorization -UserName $ManagerUserName).Id
            }

            $Body = [ordered] @{
                schemas    = @(
                    "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                )
                Operations = @(
                    foreach ($Key in $PSBoundParameters.Keys) {
                        if ($Key -in $TranslatePath.Keys) {
                            if ($TranslatePath[$Key]) {
                                $Path = $TranslatePath[$Key]
                            } else {
                                $Path = $Key
                            }
                            if ($PSBoundParameters[$Key]) {
                                $Value = $PSBoundParameters[$Key]
                            } else {
                                $Value = $null
                            }
                            if ($ActionPerProperty) {
                                if ($ActionPerProperty[$Key]) {
                                    $ActionProperty = $ActionPerProperty[$Key]
                                } else {
                                    $ActionProperty = 'replace'
                                }
                            } else {
                                $ActionProperty = 'replace'
                            }
                            [ordered] @{
                                op    = $ActionProperty
                                path  = $Path
                                value = $Value
                            }
                        }
                    }
                )
            }
        } else {

            $Body = [ordered] @{
                schemas                                                      = @(
                    "urn:ietf:params:scim:schemas:core:2.0:User"
                    "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
                    "urn:ietf:params:scim:schemas:extension:fd:2.0:User"
                )
                # Mandatory
                "id"                                                         = $Id
                "externalId"                                                 = $ExternalId
                "userName"                                                   = $UserName
                "name"                                                       = [ordered] @{
                    "familyName" = $FamilyName
                    "givenName"  = $GivenName
                }
                "displayName"                                                = $DisplayName
                "nickName"                                                   = $NickName
                "profileUrl"                                                 = $ProfileUrl
                "emails"                                                     = @(
                    if ($EmailAddressWork) {
                        @{
                            "value"   = $EmailAddressWork
                            "type"    = "work"
                            "primary" = $true
                        }
                    }
                    if ($EmailAddressHome) {
                        @{
                            "value" = $EmailAddressHome
                            "type"  = "home"
                        }
                    }
                )
                "addresses"                                                  = @(
                    if ($StreetAddress -or $Locality -or $Region -or $PostalCode -or $Country) {
                        @{
                            "streetAddress" = $StreetAddress
                            "locality"      = $Locality
                            "region"        = $Region
                            "postalCode"    = $PostalCode
                            "country"       = $Country
                            "type"          = "work"
                            "primary"       = $true
                        }
                    }
                    if ($StreetAddressHome -or $LocalityHome -or $RegionHome -or $PostalCodeHome -or $CountryHome) {
                        @{
                            "streetAddress" = $StreetAddressHome
                            "locality"      = $LocalityHome
                            "region"        = $RegionHome
                            "postalCode"    = $PostalCodeHome
                            "country"       = $CountryHome
                            "type"          = "home"
                        }
                    }
                )
                "phoneNumbers"                                               = @(
                    if ($PhoneNumberWork) {
                        @{
                            "value"   = $PhoneNumberWork
                            "type"    = "work"
                            "primary" = $true
                        }
                    }
                    if ($PhoneNumberHome) {
                        @{
                            "value" = $PhoneNumberHome
                            "type"  = "home"
                        }
                    }
                    if ($PhoneNumberMobile) {
                        @{
                            "value" = $PhoneNumberMobile
                            "type"  = "mobile"
                        }
                    }
                )
                "photos"                                                     = @(
                    if ($PhotoUrl) {
                        @{
                            "value" = $PhotoUrl
                            "type"  = "photo"
                        }
                    }
                    if ($ThumbnailUrl) {
                        @{
                            "value" = $ThumbnailUrl
                            "type"  = "thumbnail"
                        }
                    }
                )
                "password"                                                   = $Password
                "preferredLanguage"                                          = $PreferredLanguage
                "locale"                                                     = $Locale
                "timeZone"                                                   = $TimeZone
                "userType"                                                   = $UserType
                "title"                                                      = $Title
                "active"                                                     = if ($PSBoundParameters.Keys -contains ('Active')) { $Active.IsPresent } else { $Null }
                "roles"                                                      = @(
                    @{
                        "value"   = $Role
                        "display" = $Role
                    }
                )
                "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" = [ordered] @{
                    "organization"   = $Organization
                    "department"     = $Department
                    "employeeNumber" = $EmployeeNumber
                    "costCenter"     = $CostCenter
                    "division"       = $Division
                    "manager"        = @{
                        "displayName" = $ManagerDisplayName
                        "value"       = $ManagerID
                        "`$ref"       = $ManagerReference # "../v2/Users/d09420a0-e97a-11e7-9faf-236ea7c81614"
                    }
                }
                "urn:ietf:params:scim:schemas:extension:fd:2.0:User"         = [ordered] @{
                    "description"  = $Description
                    "companyId"    = $CompanyId
                    "companyLogos" = @(
                        if ($CompanyLogoUrl) {
                            @{
                                "value" = $CompanyLogoUrl
                                "type"  = "logo"
                            }
                        }
                        if ($CompanyThumbnailUrl) {
                            @{
                                "value" = $CompanyThumbnailUrl
                                "type"  = "thumbnail"
                            }
                        }
                    )
                    'directoryId'  = $DirectoryID
                    'custom01'     = $Custom01
                    'custom02'     = $Custom02
                    'custom03'     = $Custom03
                }
            }
        }
        Try {
            Remove-EmptyValue -Hashtable $Body -Recursive -Rerun 2

            $invokeRestMethodSplat = [ordered] @{
                Method      = if ($Action -eq 'Update') { 'PATCH' } else { 'PUT' }
                Uri         = "https://api.federated.directory/v2/Users/$Id"
                Headers     = [ordered]  @{
                    'Content-Type'  = 'application/json'
                    'Authorization' = $Authorization.Authorization
                    'Cache-Control' = 'no-cache'
                }
                Body        = $Body | ConvertTo-Json -Depth 10
                ErrorAction = 'Stop'
            }
            if ($DirectoryID) {
                $invokeRestMethodSplat['Headers']['directoryId'] = $DirectoryID
            }
            # for troubleshooting
            if ($VerbosePreference -eq 'Continue') {
                $Body | ConvertTo-Json -Depth 10 | Write-Verbose
            }
            $ReturnData = Invoke-RestMethod @invokeRestMethodSplat
            # don't return data as we trust it's been updated
            if (-not $Suppress) {
                $ReturnData
            }
            # # for troubleshooting
            # if ($VerbosePreference -eq 'Continue') {
            # $invokeRestMethodSplat.Remove('body')
            # $invokeRestMethodSplat | ConvertTo-Json -Depth 10 | Write-Verbose
            # }
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                $ErrorDetails = $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue
                if ($ErrorDetails.Detail -like '*userName is mandatory*') {
                    Write-Warning -Message "Set-FederatedDirectoryUser - $($ErrorDetails.Detail) [Id: $Id]"
                } else {
                    Write-Warning -Message "Set-FederatedDirectoryUser - Error $($_.Exception.Message), $($ErrorDetails.Detail)"
                }
            }
        }
    } else {
        Write-Warning -Message 'Set-FederatedDirectoryUser - No authorization found. Please make sure to use Connect-FederatedDirectory first.'
    }
}



# Export functions and aliases as required
Export-ModuleMember -Function @('Add-FederatedDirectoryUser', 'Connect-FederatedDirectory', 'Get-FederatedDirectorySchema', 'Get-FederatedDirectoryUser', 'Remove-FederatedDirectoryUser', 'Set-FederatedDirectoryUser') -Alias @('Add-FDUser', 'Connect-FD', 'Get-FDSchema', 'Get-FDUser', 'Remove-FDUser', 'Set-FDUser')
# SIG # Begin signature block
# MIInRAYJKoZIhvcNAQcCoIInNTCCJzECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB1wIWAXPUJxbaR
# 3bdNBt+vfeumfX9y42nd60Owx3dTZaCCIT0wggO3MIICn6ADAgECAhAM5+DlF9hG
# /o/lYPwb8DA5MA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBa
# Fw0zMTExMTAwMDAwMDBaMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lD
# ZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
# AQoCggEBAK0OFc7kQ4BcsYfzt2D5cRKlrtwmlIiq9M71IDkoWGAM+IDaqRWVMmE8
# tbEohIqK3J8KDIMXeo+QrIrneVNcMYQq9g+YMjZ2zN7dPKii72r7IfJSYd+fINcf
# 4rHZ/hhk0hJbX/lYGDW8R82hNvlrf9SwOD7BG8OMM9nYLxj+KA+zp4PWw25EwGE1
# lhb+WZyLdm3X8aJLDSv/C3LanmDQjpA1xnhVhyChz+VtCshJfDGYM2wi6YfQMlqi
# uhOCEe05F52ZOnKh5vqk2dUXMXWuhX0irj8BRob2KHnIsdrkVxfEfhwOsLSSplaz
# vbKX7aqn8LfFqD+VFtD/oZbrCF8Yd08CAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGG
# MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEXroq/0ksuCMS1Ri6enIZ3zbcgP
# MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUA
# A4IBAQCiDrzf4u3w43JzemSUv/dyZtgy5EJ1Yq6H6/LV2d5Ws5/MzhQouQ2XYFwS
# TFjk0z2DSUVYlzVpGqhH6lbGeasS2GeBhN9/CTyU5rgmLCC9PbMoifdf/yLil4Qf
# 6WXvh+DfwWdJs13rsgkq6ybteL59PyvztyY1bV+JAbZJW58BBZurPSXBzLZ/wvFv
# hsb6ZGjrgS2U60K3+owe3WLxvlBnt2y98/Efaww2BxZ/N3ypW2168RJGYIPXJwS+
# S86XvsNnKmgR34DnDDNmvxMNFG7zfx9jEB76jRslbWyPpbdhAbHSoyahEHGdreLD
# +cOZUbcrBwjOLuZQsqf6CkUvovDyMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1
# b5VQCDANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln
# aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtE
# aWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgx
# MDIyMTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j
# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBT
# SEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEF
# AAOCAQ8AMIIBCgKCAQEA+NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLX
# cep2nQUut4/6kkPApfmJ1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSR
# I5aQd4L5oYQjZhJUM1B0sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXi
# TWAYvqrEsq5wMWYzcT6scKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5
# Ng2Q7+S1TqSp6moKq4TzrGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8
# vYWxYoNzQYIH5DiLanMg0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYD
# VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYB
# BQUHAwMweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k
# aWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4
# oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
# b3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dEFzc3VyZWRJRFJvb3RDQS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCow
# KAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZI
# AYb9bAMwHQYDVR0OBBYEFFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaA
# FEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPz
# ItEVyCx8JSl2qB1dHC06GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRu
# pY5a4l4kgU4QpO4/cY5jDhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKN
# JK4kxscnKqEpKBo6cSgCPC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmif
# z0DLQESlE/DmZAwlCEIysjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN
# 3fYBIM6ZMWM9CBoYs4GbT8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKy
# ZqHnGKSaZFHvMIIFPTCCBCWgAwIBAgIQBNXcH0jqydhSALrNmpsqpzANBgkqhkiG
# 9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw
# FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEy
# IEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMDYyNjAwMDAwMFoXDTIz
# MDcwNzEyMDAwMFowejELMAkGA1UEBhMCUEwxEjAQBgNVBAgMCcWabMSFc2tpZTER
# MA8GA1UEBxMIS2F0b3dpY2UxITAfBgNVBAoMGFByemVteXPFgmF3IEvFgnlzIEVW
# T1RFQzEhMB8GA1UEAwwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMIIBIjANBgkq
# hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7KB3iyBrhkLUbbFe9qxhKKPBYqDBqln
# r3AtpZplkiVjpi9dMZCchSeT5ODsShPuZCIxJp5I86uf8ibo3vi2S9F9AlfFjVye
# 3dTz/9TmCuGH8JQt13ozf9niHecwKrstDVhVprgxi5v0XxY51c7zgMA2g1Ub+3ti
# i0vi/OpmKXdL2keNqJ2neQ5cYly/GsI8CREUEq9SZijbdA8VrRF3SoDdsWGf3tZZ
# zO6nWn3TLYKQ5/bw5U445u/V80QSoykszHRivTj+H4s8ABiforhi0i76beA6Ea41
# zcH4zJuAp48B4UhjgRDNuq8IzLWK4dlvqrqCBHKqsnrF6BmBrv+BXQIDAQABo4IB
# xTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYE
# FBixNSfoHFAgJk4JkDQLFLRNlJRmMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK
# BggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2Vy
# dC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQu
# ZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUwQzA3
# BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu
# Y29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNpZ25p
# bmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAmr1sz4ls
# LARi4wG1eg0B8fVJFowtect7SnJUrp6XRnUG0/GI1wXiLIeow1UPiI6uDMsRXPHU
# F/+xjJw8SfIbwava2eXu7UoZKNh6dfgshcJmo0QNAJ5PIyy02/3fXjbUREHINrTC
# vPVbPmV6kx4Kpd7KJrCo7ED18H/XTqWJHXa8va3MYLrbJetXpaEPpb6zk+l8Rj9y
# G4jBVRhenUBUUj3CLaWDSBpOA/+sx8/XB9W9opYfYGb+1TmbCkhUg7TB3gD6o6ES
# Jre+fcnZnPVAPESmstwsT17caZ0bn7zETKlNHbc1q+Em9kyBjaQRcEQoQQNpezQu
# g9ufqExx6lHYDjCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZI
# hvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
# MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNz
# dXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVow
# YjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290
# IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjww
# IjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J5
# 8soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMH
# hOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6
# Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQ
# ecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4b
# A3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9
# WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCU
# tNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvo
# ZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/J
# vNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCP
# orF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMB
# Af8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXr
# oq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRt
# MGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEF
# BQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgw
# BgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cH
# vZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8
# UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTn
# f+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxU
# jG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8j
# LfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDCCBq4w
# ggSWoAMCAQICEAc2N7ckVHzYR6z9KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkG
# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4X
# DTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAV
# BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVk
# IEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcN
# AQEBBQADggIPADCCAgoCggIBAMaGNQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5M
# om2gsMyD+Vr2EaFEFUJfpIjzaPp985yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE
# 2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWN
# lCnT2exp39mQh0YAe9tEQYncfGpXevA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFo
# bjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhN
# ef7Xj3OTrCw54qVI1vCwMROpVymWJy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3Vu
# JyWQmDo4EbP29p7mO1vsgd4iFNmCKseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtz
# Q87fSqEcazjFKfPKqpZzQmiftkaznTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4O
# uGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5
# sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm
# 4T72wnSyPx4JduyrXUZ14mCjWAkBKAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIz
# tM2xAgMBAAGjggFdMIIBWTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6
# FtltTYUvcyl2mi91jGogj57IbzAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qY
# rhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYB
# BQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20w
# QQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwz
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZ
# MBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmO
# wJO2b5ipRCIBfmbW2CFC4bAYLhBNE88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H
# 6T5gyNgL5Vxb122H+oQgJTQxZ822EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/
# R3f7cnQU1/+rT4osequFzUNf7WC2qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzv
# qLx1T7pa96kQsl3p/yhUifDVinF2ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/ae
# sXmZgaNWhqsKRcnfxI2g55j7+6adcq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdm
# kfFynOlLAlKnN36TU6w7HQhJD5TNOXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3
# EyTN3B14OuSereU0cZLXJmvkOHOrpgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh
# 3SP9HSjTx/no8Zhf+yvYfvJGnXUsHicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA
# 3yAWTyf7YGcWoWa63VXAOimGsJigK+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8
# BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsf
# gPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr9u3WfPwwggbGMIIErqADAgECAhAKekqI
# nsmZQpAGYzhNhpedMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYD
# VQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBH
# NCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjIwMzI5MDAwMDAw
# WhcNMzMwMzE0MjM1OTU5WjBMMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
# cnQsIEluYy4xJDAiBgNVBAMTG0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIyIC0gMjCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALkqliOmXLxf1knwFYIY9DPu
# zFxs4+AlLtIx5DxArvurxON4XX5cNur1JY1Do4HrOGP5PIhp3jzSMFENMQe6Rm7p
# o0tI6IlBfw2y1vmE8Zg+C78KhBJxbKFiJgHTzsNs/aw7ftwqHKm9MMYW2Nq867Lx
# g9GfzQnFuUFqRUIjQVr4YNNlLD5+Xr2Wp/D8sfT0KM9CeR87x5MHaGjlRDRSXw9Q
# 3tRZLER0wDJHGVvimC6P0Mo//8ZnzzyTlU6E6XYYmJkRFMUrDKAz200kheiClOEv
# A+5/hQLJhuHVGBS3BEXz4Di9or16cZjsFef9LuzSmwCKrB2NO4Bo/tBZmCbO4O2u
# fyguwp7gC0vICNEyu4P6IzzZ/9KMu/dDI9/nw1oFYn5wLOUrsj1j6siugSBrQ4nI
# fl+wGt0ZvZ90QQqvuY4J03ShL7BUdsGQT5TshmH/2xEvkgMwzjC3iw9dRLNDHSNQ
# zZHXL537/M2xwafEDsTvQD4ZOgLUMalpoEn5deGb6GjkagyP6+SxIXuGZ1h+fx/o
# K+QUshbWgaHK2jCQa+5vdcCwNiayCDv/vb5/bBMY38ZtpHlJrYt/YYcFaPfUcONC
# leieu5tLsuK2QT3nr6caKMmtYbCgQRgZTu1Hm2GV7T4LYVrqPnqYklHNP8lE54CL
# KUJy93my3YTqJ+7+fXprAgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYD
# VR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgG
# BmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxq
# II+eyG8wHQYDVR0OBBYEFI1kt4kh/lZYRIRhp+pvHDaP3a8NMFoGA1UdHwRTMFEw
# T6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
# NFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGD
# MIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYB
# BQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0
# ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQEL
# BQADggIBAA0tI3Sm0fX46kuZPwHk9gzkrxad2bOMl4IpnENvAS2rOLVwEb+EGYs/
# XeWGT76TOt4qOVo5TtiEWaW8G5iq6Gzv0UhpGThbz4k5HXBw2U7fIyJs1d/2Wcuh
# wupMdsqh3KErlribVakaa33R9QIJT4LWpXOIxJiA3+5JlbezzMWn7g7h7x44ip/v
# EckxSli23zh8y/pc9+RTv24KfH7X3pjVKWWJD6KcwGX0ASJlx+pedKZbNZJQfPQX
# podkTz5GiRZjIGvL8nvQNeNKcEiptucdYL0EIhUlcAZyqUQ7aUcR0+7px6A+TxC5
# MDbk86ppCaiLfmSiZZQR+24y8fW7OK3NwJMR1TJ4Sks3KkzzXNy2hcC7cDBVeNaY
# /lRtf3GpSBp43UZ3Lht6wDOK+EoojBKoc88t+dMj8p4Z4A2UKKDr2xpRoJWCjihr
# pM6ddt6pc6pIallDrl/q+A8GQp3fBmiW/iqgdFtjZt5rLLh4qk1wbfAs8QcVfjW0
# 5rUMopml1xVrNQ6F1uAszOAMJLh8UgsemXzvyMjFjFhpr6s94c/MfRWuFL+Kcd/K
# l7HYR+ocheBFThIcFClYzG/Tf8u+wQ5KbyCcrtlzMlkI5y2SoRoR/jKYpl0rl+CL
# 05zMbbUNrkdjOEcXW28T2moQbh9Jt0RbtAgKh1pZBHYRoad3AhMcMYIFXTCCBVkC
# AQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBB
# c3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQBNXcH0jqydhSALrNmpsqpzANBglg
# hkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3
# DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEV
# MC8GCSqGSIb3DQEJBDEiBCAxJdh2vLBHKz+dcz4PAWxFsoSqL7AVIAASfMuDhGXL
# VTANBgkqhkiG9w0BAQEFAASCAQB7v4/KopP9iEE4/jQcBvVTs5NNqla63HqCZrze
# SeiT3ugzBke/BEze5mCvHXUuA5reChl5L1Q5iYfdnzsrkNesRrb3bcls5CTJrbGA
# sn3SIeh5JjiQ/63BJmxF3OBOG1XPA5KJ8XBXD9Idw4uHK2XaWHlbcEyI+5pcLgHV
# GE90z9GZ1XacjlzhkVIfZcc1lZTMoLIf3iaXhcvYvdfFOk4SUL/UyFNh8S6zlqpc
# cNPIhdJM38uTUhi6DHNWSQY9Ph0ehCs9JiFJcqLsoH8T2utkbZl4Sqsv2IQWiBoW
# R93YdUIasSIIfoeLyZLUJv3MecaLcsjCt+h6zxTDEFL8G6vYoYIDIDCCAxwGCSqG
# SIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRp
# Z2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQw
# OTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQCnpKiJ7JmUKQBmM4TYaXnTANBglg
# hkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcN
# AQkFMQ8XDTIyMDkxMTEyNTY0OFowLwYJKoZIhvcNAQkEMSIEIE8ynnYKoG87k9i6
# n52/mhbFiwYtrvt1KRDvq/cfXInKMA0GCSqGSIb3DQEBAQUABIICAFshh/VwIAsX
# bNdka9V/iXtg5j9pgTgmWaJb7g/OYN+6sZeEYMjrNICU7xEcNIoZoeoHPpbe8p7Z
# qi5BZDLZZxUKPPJ+JOyjr3xfnuNSS1ld6ecBvYOSnnkbfwcfEK8/L0+SrLwKL+I9
# SAeW+WGlLGJHGhOXsq4jlX2co6VQxnBWSiSzToPveGNMs1mWl/2/6OQq7029CtuN
# 0mwBsef0W9mTECD79zspLwTRT+Cn3rDk3yemtZagV1tJxuKON0JJWTdPFFSyRzJY
# PYZHDsg9OPPJwETLaVPjXqGcTHAzx7ZfmBvZ5vRXaohrLeQiCgDc1DObSv/B1c/g
# TO1Asmqb6hLVKSBqVKYwQlup7VbUh0lEjSsF20RBsDdIXV5VELPioTjO40FYHCye
# HNrLzv8izxDs/XO0QCHtNbkV8ImB1hp5gm5S4gHw1rqlWaWXacKz60J/phLjVbI1
# 5iMZt2lqrDIf62UMajP6TQe5AOHv0AkJDUr3LIsxVeGEmlFTJ8ZplIZuTIb/Rssv
# NRC4OabNHJS1BXoBRaFHfBxltSP5BrPotuZQ/o3WSNXY8sJXZSMjQ/KZrX9AyiLS
# Sj9aWDETqhF0R/nj29sZGQnoN9iYhVoM+pYemkLxwh5K2y1/uSx5aOIojabohxaz
# eCgHjFETY8ghKfC/gCHgCA/IYwsBWK8D
# SIG # End signature block