Microsoft.PowerApps.AuthModule.psm1
$local:ErrorActionPreference = "Stop" <# If(Get-Module -ListAvailable -Name (Join-Path (Split-Path $script:MyInvocation.MyCommand.Path) "Microsoft.PowerApps.RestClientModule.psm1")) { Write-Host "Module loaded" } else { Import-Module (Join-Path (Split-Path $script:MyInvocation.MyCommand.Path) "Microsoft.PowerApps.RestClientModule.psm1") -NoClobber #-Force } #> #[Reflection.Assembly]::LoadFile("$(Split-Path $script:MyInvocation.MyCommand.Path)\Microsoft.IdentityModel.Clients.ActiveDirectory.dll") | Out-Null #[Reflection.Assembly]::LoadFile("$(Split-Path $script:MyInvocation.MyCommand.Path)\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll") | Out-Null function Get-JwtTokenClaims { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string]$JwtToken ) $tokenSplit = $JwtToken.Split(".") $claimsSegment = $tokenSplit[1].Replace(" ", "+"); $mod = $claimsSegment.Length % 4 if ($mod -gt 0) { $paddingCount = 4 - $mod; for ($i = 0; $i -lt $paddingCount; $i++) { $claimsSegment += "=" } } $decodedClaimsSegment = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($claimsSegment)) return ConvertFrom-Json $decodedClaimsSegment } function Add-PowerAppsAccount { [CmdletBinding()] param ( [string] $Audience = "https://management.azure.com/", [Parameter(Mandatory = $false)] [ValidateSet("prod","preview","tip1", "tip2", "usgov", "usgovhigh")] [string]$Endpoint = "prod", [string]$Username = $null, [SecureString]$Password = $null ) $authContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.windows.net/common"); $redirectUri = New-Object System.Uri("urn:ietf:wg:oauth:2.0:oob"); if ($Username -ne $null -and $Password -ne $null) { $credential = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential($Username, $Password) $authResult = $authContext.AcquireToken($Audience, "1950a258-227b-4e31-a9cf-717495945fc2", $credential); } else { $authResult = $authContext.AcquireToken($Audience, "1950a258-227b-4e31-a9cf-717495945fc2", $redirectUri, 1); } $claims = Get-JwtTokenClaims -JwtToken $authResult.IdToken $global:currentSession = @{ loggedIn = $true; idToken = $authResult.IdToken; upn = $claims.upn; tenantId = $claims.tid; userId = $claims.oid; refreshToken = $authResult.RefreshToken; expiresOn = (Get-Date).AddHours(8); resourceTokens = @{ $Audience = @{ accessToken = $authResult.AccessToken; expiresOn = $authResult.ExpiresOn; } }; selectedEnvironment = "~default"; flowEndpoint = switch ($Endpoint) { "prod" { "api.flow.microsoft.com" } "usgov" { "gov.api.flow.microsoft.us" } "usgovhigh" { "high.api.flow.microsoft.us" } "preview" { "preview.api.flow.microsoft.com" } "tip1" { "tip1.api.flow.microsoft.com"} "tip2" { "tip2.api.flow.microsoft.com" } default { throw "Unsupported endpoint '$Endpoint'"} }; powerAppsEndpoint = switch ($Endpoint) { "prod" { "api.powerapps.com" } "usgov" { "gov.api.powerapps.us" } "usgovhigh" { "high.api.powerapps.us" } "preview" { "preview.api.powerapps.com" } "tip1" { "tip1.api.powerapps.com"} "tip2" { "tip2.api.powerapps.com" } default { throw "Unsupported endpoint '$Endpoint'"} }; bapEndpoint = switch ($Endpoint) { "prod" { "api.bap.microsoft.com" } "usgov" { "gov.api.bap.microsoft.us" } "usgovhigh" { "high.api.bap.microsoft.us" } "preview" { "preview.api.bap.microsoft.com" } "tip1" { "tip1.api.bap.microsoft.com"} "tip2" { "tip2.api.bap.microsoft.com" } default { throw "Unsupported endpoint '$Endpoint'"} }; graphEndpoint = switch ($Endpoint) { "prod" { "graph.windows.net" } "usgov" { "graph.windows.net" } "usgovhigh" { "graph.windows.net" } "preview" { "graph.windows.net" } "tip1" { "graph.windows.net"} "tip2" { "graph.windows.net" } default { throw "Unsupported endpoint '$Endpoint'"} }; cdsOneEndpoint = switch ($Endpoint) { "prod" { "api.cds.microsoft.com" } "usgov" { "gov.api.cds.microsoft.us" } "usgovhigh" { "high.api.cds.microsoft.us" } "preview" { "preview.api.cds.microsoft.com" } "tip1" { "tip1.api.cds.microsoft.com"} "tip2" { "tip2.api.cds.microsoft.com" } default { throw "Unsupported endpoint '$Endpoint'"} }; }; } function Test-PowerAppsAccount { [CmdletBinding()] param ( ) if (-not $global:currentSession) { Add-PowerAppsAccount } } function Remove-PowerAppsAccount { [CmdletBinding()] param ( ) if ($global:currentSession -ne $null -and $global:currentSession.upn -ne $null) { Write-Verbose "Logging out $($global:currentSession.upn)" } else { Write-Verbose "No user logged in" } $global:currentSession = @{ loggedIn = $false; }; } function Get-JwtToken { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $Audience ) if ($global:currentSession -eq $null) { $global:currentSession = @{ loggedIn = $false; }; } if ($global:currentSession.loggedIn -eq $false -or $global:currentSession.expiresOn -lt (Get-Date)) { Write-Verbose "No user logged in. Signing the user in before acquiring token." Add-PowerAppsAccount -Audience $Audience } if ($global:currentSession.resourceTokens[$Audience] -eq $null -or ` $global:currentSession.resourceTokens[$Audience].accessToken -eq $null -or ` $global:currentSession.resourceTokens[$Audience].expiresOn -eq $null -or ` $global:currentSession.resourceTokens[$Audience].expiresOn -lt (Get-Date)) { Write-Verbose "Token for $Audience is either missing or expired. Acquiring a new one." $tenantId = $global:currentSession.tenantId $authContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.windows.net/$tenantId"); $refreshTokenResult = $authContext.AcquireTokenByRefreshToken($global:currentSession.refreshToken, "1950a258-227b-4e31-a9cf-717495945fc2", $Audience) $global:currentSession.resourceTokens[$Audience] = @{ accessToken = $refreshTokenResult.AccessToken; expiresOn = $refreshTokenResult.ExpiresOn; } } return $global:currentSession.resourceTokens[$Audience].accessToken; } function Invoke-OAuthDialog { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $ConsentLinkUri ) Add-Type -AssemblyName System.Windows.Forms $form = New-Object -TypeName System.Windows.Forms.Form -Property @{ Width=440; Height=640 } $web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{ Width=420; Height=600; Url=$ConsentLinkUri } $DocComp = { $Global:uri = $web.Url.AbsoluteUri if ($Global:uri -match "error=[^&]*|code=[^&]*") { $form.Close() } } $web.ScriptErrorsSuppressed = $true $web.Add_DocumentCompleted($DocComp) $form.Controls.Add($web) $form.Add_Shown({$form.Activate()}) $form.ShowDialog() | Out-Null $queryOutput = [System.Web.HttpUtility]::ParseQueryString($web.Url.Query) $output = @{} foreach($key in $queryOutput.Keys) { $output["$key"] = $queryOutput[$key] } return $output } function Get-TenantDetailsFromGraph { <# .SYNOPSIS . .DESCRIPTION The Get-TenantDetailsFromGraph function . Use Get-Help Get-TenantDetailsFromGraph -Examples for more detail. .EXAMPLE Get-TenantDetailsFromGraph . #> param ( [string]$GraphApiVersion = "1.6" ) process { $TenantIdentifier = "myorganization" $route = "https://{graphEndpoint}/{tenantIdentifier}/tenantDetails`?api-version={graphApiVersion}" ` | ReplaceMacro -Macro "{tenantIdentifier}" -Value $TenantIdentifier ` | ReplaceMacro -Macro "{graphApiVersion}" -Value $GraphApiVersion; $graphResponse = InvokeApi -Method GET -Route $route CreateTenantObject -TenantObj $graphResponse.value } } #Returns users or groups from Graph #wrapper on top of https://msdn.microsoft.com/en-us/library/azure/ad/graph/api/users-operations & https://msdn.microsoft.com/en-us/library/azure/ad/graph/api/groups-operations function Get-UsersOrGroupsFromGraph( ) { [CmdletBinding(DefaultParameterSetName="Id")] param ( [Parameter(Mandatory = $true, ParameterSetName = "Id")] [string]$ObjectId, [Parameter(Mandatory = $true, ParameterSetName = "Search")] [string]$SearchString, [Parameter(Mandatory = $false, ParameterSetName = "Search")] [Parameter(Mandatory = $false, ParameterSetName = "Id")] [string]$GraphApiVersion = "1.6" ) Process { if (-not [string]::IsNullOrWhiteSpace($ObjectId)) { $userGraphUri = "https://graph.windows.net/myorganization/users/{userId}`?&api-version={graphApiVersion}" ` | ReplaceMacro -Macro "{userId}" -Value $ObjectId ` | ReplaceMacro -Macro "{graphApiVersion}" -Value $GraphApiVersion; $userGraphResponse = InvokeApi -Route $userGraphUri -Method GET If($userGraphResponse.StatusCode -eq $null) { CreateUserObject -UserObj $userGraphResponse } $groupsGraphUri = "https://graph.windows.net/myorganization/groups/{groupId}`?api-version={graphApiVersion}" ` | ReplaceMacro -Macro "{groupId}" -Value $ObjectId ` | ReplaceMacro -Macro "{graphApiVersion}" -Value $GraphApiVersion; $groupGraphResponse = InvokeApi -Route $groupsGraphUri -Method GET If($groupGraphResponse.StatusCode -eq $null) { CreateGroupObject -GroupObj $groupGraphResponse } } else { $userFilter = "startswith(userPrincipalName,'$SearchString') or startswith(displayName,'$SearchString')" $userGraphUri = "https://graph.windows.net/myorganization/users`?`$filter={filter}&api-version={graphApiVersion}" ` | ReplaceMacro -Macro "{filter}" -Value $userFilter ` | ReplaceMacro -Macro "{graphApiVersion}" -Value $GraphApiVersion; $userGraphResponse = InvokeApi -Route $userGraphUri -Method GET foreach($user in $userGraphResponse.value) { CreateUserObject -UserObj $user } $groupFilter = "startswith(displayName,'$SearchString')" $groupsGraphUri = "https://graph.windows.net/myorganization/groups`?`$filter={filter}&api-version={graphApiVersion}" ` | ReplaceMacro -Macro "{filter}" -Value $groupFilter ` | ReplaceMacro -Macro "{graphApiVersion}" -Value $GraphApiVersion; $groupsGraphResponse = Invoke-Request -Uri $groupsGraphUri -Method GET -ParseContent -ThrowOnFailure foreach($group in $groupsGraphResponse.value) { CreateGroupObject -GroupObj $group } } } } function CreateUserObject { param ( [Parameter(Mandatory = $true)] [object]$UserObj ) return New-Object -TypeName PSObject ` | Add-Member -PassThru -MemberType NoteProperty -Name ObjectType -Value $UserObj.objectType ` | Add-Member -PassThru -MemberType NoteProperty -Name ObjectId -Value $UserObj.objectId ` | Add-Member -PassThru -MemberType NoteProperty -Name UserPrincipalName -Value $UserObj.userPrincipalName ` | Add-Member -PassThru -MemberType NoteProperty -Name Mail -Value $UserObj.mail ` | Add-Member -PassThru -MemberType NoteProperty -Name DisplayName -Value $UserObj.displayName ` | Add-Member -PassThru -MemberType NoteProperty -Name AssignedLicenses -Value $UserObj.assignedLicenses ` | Add-Member -PassThru -MemberType NoteProperty -Name AssignedPlans -Value $UserObj.assignedLicenses ` | Add-Member -PassThru -MemberType NoteProperty -Name Internal -Value $UserObj; } function CreateGroupObject { param ( [Parameter(Mandatory = $true)] [object]$GroupObj ) return New-Object -TypeName PSObject ` | Add-Member -PassThru -MemberType NoteProperty -Name ObjectType -Value $GroupObj.objectType ` | Add-Member -PassThru -MemberType NoteProperty -Name Objectd -Value $GroupObj.objectId ` | Add-Member -PassThru -MemberType NoteProperty -Name Mail -Value $GroupObj.mail ` | Add-Member -PassThru -MemberType NoteProperty -Name DisplayName -Value $GroupObj.displayName ` | Add-Member -PassThru -MemberType NoteProperty -Name Internal -Value $GroupObj; } function CreateTenantObject { param ( [Parameter(Mandatory = $true)] [object]$TenantObj ) return New-Object -TypeName PSObject ` | Add-Member -PassThru -MemberType NoteProperty -Name ObjectType -Value $TenantObj.objectType ` | Add-Member -PassThru -MemberType NoteProperty -Name TenantId -Value $TenantObj.objectId ` | Add-Member -PassThru -MemberType NoteProperty -Name Country -Value $TenantObj.countryLetterCode ` | Add-Member -PassThru -MemberType NoteProperty -Name Language -Value $TenantObj.preferredLanguage ` | Add-Member -PassThru -MemberType NoteProperty -Name DisplayName -Value $TenantObj.displayName ` | Add-Member -PassThru -MemberType NoteProperty -Name Domains -Value $TenantObj.verifiedDomains ` | Add-Member -PassThru -MemberType NoteProperty -Name Internal -Value $TenantObj; } # SIG # Begin signature block # MIIdiAYJKoZIhvcNAQcCoIIdeTCCHXUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUv/9OItZ38P272nP5PH7qrFRq # bwWgghhyMIIE3jCCA8agAwIBAgITMwAAATGN4b6nnwo14AAAAAABMTANBgkqhkiG # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTkwOTA2MjA0MDA2 # WhcNMjAxMjA0MjA0MDA2WjCBzjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJp # Y28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjE0OEMtQzRCOS0yMDY2MSUwIwYD # VQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIBIjANBgkqhkiG9w0B # AQEFAAOCAQ8AMIIBCgKCAQEA44MW746WlNnuhV4MNgY8hZbuKvs8jXFCQGmtGbkk # jAXvOntKezUhFewTM3cTzk9Y7ktGmX/IXwdVf4p4/EijDrtP/0sM2VT/WUCSQ0OW # Lf3t1uN4+5Q/J4xBoWOLu8wyJplsj1LVk2c+1ZBuRi9lWMcKd0z+bOf3N5DBDYqF # p/YiUmZ1eg+/mIDvUfXzIL7YQ3m5GwxujGjDlXsGD3ZLtJ7ubl/NPJASHIrZ+JnD # zLWybUeGQFwmGbHcOTt8o2rGO4+B5NKtJYveB3VoBWKwPgknL0HD12MMH2gXYyFS # FVGlESd1Wmig51WsEXf3Derh0zCZTklL8V3e6prwe09aaQIDAQABo4IBCTCCAQUw # HQYDVR0OBBYEFC1z8U9/hhkUeN6Hw36S/RTKrvmaMB8GA1UdIwQYMBaAFCM0+NlS # RnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly9jcmwubWlj # cm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY3Jvc29mdFRpbWVTdGFtcFBD # QS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsGAQUFBzAChjxodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcnQw # EwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQEFBQADggEBAGxFjYUTibWy # u6Lq9jxdFDXSk32GAl1spdfdYGtetReYauvnqwMNAlOfjvb3vaHXhwtFyjpH+zKG # 74+/POkWo0Ml8sYKH0d8HMSx0jkNp9wVBkn2vOt8NS2PIODI9j/ib7pE/1nqjbf6 # rIv0JiyGaOq4654Dt5Gv636uYyMnrh+DBi41vOsbJiBqaojbmCOXf5VHro91joUp # 27lIUCBcSXG2fYSO3BSso1wfJzdUxgffRotNCr1o9oIIf4lgaPomPJNpDA3nOSQc # NwWC1pkQUE+PSPWFMYxFddUTfiR9DN2J9rzgIGCCSHJAUEGFDW5hgYX7RtIa736g # w3Uwm+op7jQwggYDMIID66ADAgECAhMzAAABUptAn1BWmXWIAAAAAAFSMA0GCSqG # SIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwHhcNMTkw # NTAyMjEzNzQ2WhcNMjAwNTAyMjEzNzQ2WjB0MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24w # ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxp4nT9qfu9O10iJyewYXH # lN+WEh79Noor9nhM6enUNbCbhX9vS+8c/3eIVazSYnVBTqLzW7xWN1bCcItDbsEz # KEE2BswSun7J9xCaLwcGHKFr+qWUlz7hh9RcmjYSkOGNybOfrgj3sm0DStoK8ljw # EyUVeRfMHx9E/7Ca/OEq2cXBT3L0fVnlEkfal310EFCLDo2BrE35NGRjG+/nnZiq # KqEh5lWNk33JV8/I0fIcUKrLEmUGrv0CgC7w2cjmbBhBIJ+0KzSnSWingXol/3iU # dBBy4QQNH767kYGunJeY08RjHMIgjJCdAoEM+2mXv1phaV7j+M3dNzZ/cdsz3oDf # AgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEEAYI3TAgBBggrBgEFBQcDAzAd # BgNVHQ4EFgQU3f8Aw1sW72WcJ2bo/QSYGzVrRYcwVAYDVR0RBE0wS6RJMEcxLTAr # BgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEWMBQG # A1UEBRMNMjMwMDEyKzQ1NDEzNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzcitW2o # ynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v # cGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEGCCsG # AQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0MAwG # A1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAJTwROaHvogXgixWjyjvLfiR # gqI2QK8GoG23eqAgNjX7V/WdUWBbs0aIC3k49cd0zdq+JJImixcX6UOTpz2LZPFS # h23l0/Mo35wG7JXUxgO0U+5drbQht5xoMl1n7/TQ4iKcmAYSAPxTq5lFnoV2+fAe # ljVA7O43szjs7LR09D0wFHwzZco/iE8Hlakl23ZT7FnB5AfU2hwfv87y3q3a5qFi # ugSykILpK0/vqnlEVB0KAdQVzYULQ/U4eFEjnis3Js9UrAvtIhIs26445Rj3UP6U # 4GgOjgQonlRA+mDlsh78wFSGbASIvK+fkONUhvj8B8ZHNn4TFfnct+a0ZueY4f6a # RPxr8beNSUKn7QW/FQmn422bE7KfnqWncsH7vbNhG929prVHPsaa7J22i9wyHj7m # 0oATXJ+YjfyoEAtd5/NyIYaE4Uu0j1EhuYUo5VaJJnMaTER0qX8+/YZRWrFN/hep # s41XNVjiAawpbAa0fUa3R9RNBjPiBnM0gvNPorM4dsV2VJ8GluIQOrJlOvuCrOYD # GirGnadOmQ21wPBoGFCWpK56PxzliKsy5NNmAXcEx7Qb9vUjY1WlYtrdwOXTpxN4 # slzIht69BaZlLIjLVWwqIfuNrhHKNDM9K+v7vgrIbf7l5/665g0gjQCDCN6Q5sxu # ttTAEKtJeS/pkpI+DbZ/MIIGBzCCA++gAwIBAgIKYRZoNAAAAAAAHDANBgkqhkiG # 9w0BAQUFADBfMRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJ # bWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBB # dXRob3JpdHkwHhcNMDcwNDAzMTI1MzA5WhcNMjEwNDAzMTMwMzA5WjB3MQswCQYD # VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe # MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3Nv # ZnQgVGltZS1TdGFtcCBQQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQCfoWyx39tIkip8ay4Z4b3i48WZUSNQrc7dGE4kD+7Rp9FMrXQwIBHrB9VUlRVJ # lBtCkq6YXDAm2gBr6Hu97IkHD/cOBJjwicwfyzMkh53y9GccLPx754gd6udOo6HB # I1PKjfpFzwnQXq/QsEIEovmmbJNn1yjcRlOwhtDlKEYuJ6yGT1VSDOQDLPtqkJAw # bofzWTCd+n7Wl7PoIZd++NIT8wi3U21StEWQn0gASkdmEScpZqiX5NMGgUqi+YSn # EUcUCYKfhO1VeP4Bmh1QCIUAEDBG7bfeI0a7xC1Un68eeEExd8yb3zuDk6FhArUd # DbH895uyAc4iS1T/+QXDwiALAgMBAAGjggGrMIIBpzAPBgNVHRMBAf8EBTADAQH/ # MB0GA1UdDgQWBBQjNPjZUkZwCu1A+3b7syuwwzWzDzALBgNVHQ8EBAMCAYYwEAYJ # KwYBBAGCNxUBBAMCAQAwgZgGA1UdIwSBkDCBjYAUDqyCYEBWJ5flJRP8KuEKU5VZ # 5KShY6RhMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJkiaJk/IsZAEZFglt # aWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1 # dGhvcml0eYIQea0WoUqgpa1Mc1j0BxMuZTBQBgNVHR8ESTBHMEWgQ6BBhj9odHRw # Oi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9taWNyb3NvZnRy # b290Y2VydC5jcmwwVAYIKwYBBQUHAQEESDBGMEQGCCsGAQUFBzAChjhodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jvc29mdFJvb3RDZXJ0LmNy # dDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQUFAAOCAgEAEJeKw1wD # RDbd6bStd9vOeVFNAbEudHFbbQwTq86+e4+4LtQSooxtYrhXAstOIBNQmd16QOJX # u69YmhzhHQGGrLt48ovQ7DsB7uK+jwoFyI1I4vBTFd1Pq5Lk541q1YDB5pTyBi+F # A+mRKiQicPv2/OR4mS4N9wficLwYTp2OawpylbihOZxnLcVRDupiXD8WmIsgP+IH # GjL5zDFKdjE9K3ILyOpwPf+FChPfwgphjvDXuBfrTot/xTUrXqO/67x9C0J71FNy # Ie4wyrt4ZVxbARcKFA7S2hSY9Ty5ZlizLS/n+YWGzFFW6J1wlGysOUzU9nm/qhh6 # YinvopspNAZ3GmLJPR5tH4LwC8csu89Ds+X57H2146SodDW4TsVxIxImdgs8Uoxx # WkZDFLyzs7BNZ8ifQv+AeSGAnhUwZuhCEl4ayJ4iIdBD6Svpu/RIzCzU2DKATCYq # SCRfWupW76bemZ3KOm+9gSd0BhHudiG/m4LBJ1S2sWo9iaF2YbRuoROmv6pH8BJv # /YoybLL+31HIjCPJZr2dHYcSZAI9La9Zj7jkIeW1sMpjtHhUBdRBLlCslLCleKuz # oJZ1GtmShxN1Ii8yqAhuoFuMJb+g74TKIdbrHk/Jmu5J4PcBZW+JC33Iacjmbuqn # l84xKf8OxVtc2E0bodj6L54/LlUWa8kTo/0wggd6MIIFYqADAgECAgphDpDSAAAA # AAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBB # dXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDlaMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgGOBoE # Sbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S35tT # sgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jzy23z # OlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/74yta # EB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2uM1jF # tz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33X/DQ # Ur+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIlXdMh # Sz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP6SNJ # vBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLBl4F7 # 7dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGFRInE # CUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiMCwID # AQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQBdOC # qhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQE # AwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO4eqn # xzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNv # bS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18yMi5j # cmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1pY3Jv # c29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18yMi5j # cnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIBFjNo # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNwcy5o # dG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkAXwBz # AHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY4FR5 # Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj82nb # Y78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUdd5Q5 # 4ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJYx8J # aW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYfwzIY # 4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJaG5v # p7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1jNpeG # 39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9Bxw4o # 7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96eiL6 # SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7r/ww # 7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5IRcBC # yZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIEgDCCBHwCAQEwgZUwfjELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0 # IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAVKbQJ9QVpl1iAAAAAABUjAJBgUr # DgMCGgUAoIGUMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC # AQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBRLCgrJY3pfYGMH6GlL # ig5oSyA72TA0BgorBgEEAYI3AgEMMSYwJKASgBAAVABlAHMAdABTAGkAZwBuoQ6A # DGh0dHA6Ly90ZXN0IDANBgkqhkiG9w0BAQEFAASCAQAdoZVy1WdV96PPYylDdF6r # /mjKuvZYhgZpgLltKxP+2YGUtfpgsTuTegiL5nw1mV4ZYbX62Qw1KOWVIKozkXfE # ojPjrIQyfvA4qxXZAZieHsgO+DG2VjFHGHotVmj/ynZBVyvOpHHGw7xcLYJPR7h8 # z1NR9866v8KeYl84TpeozXYuM/HXzAuwiA4ZteVPiOdaV2KMuOzFasmemGkttXsj # jPtan7QPXT5gAaxiEalVou8t4wvFoB6+XhSkmMQnRJxnrSaehdejXufOxSK8TObu # z3aTRj8M8T4qgL9StOtoB2I3UDIuAOUcdYdH1+WCkQYfMTIIoczcexXsMWmgG/eA # oYICKDCCAiQGCSqGSIb3DQEJBjGCAhUwggIRAgEBMIGOMHcxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAfBgNVBAMTGE1pY3Jvc29mdCBUaW1l # LVN0YW1wIFBDQQITMwAAATGN4b6nnwo14AAAAAABMTAJBgUrDgMCGgUAoF0wGAYJ # KoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkxMjIwMjEz # MDI4WjAjBgkqhkiG9w0BCQQxFgQUvFbzqT97KaM8bKz+BvYWTYTLk28wDQYJKoZI # hvcNAQEFBQAEggEAz+t9M5hGY7K1pYXLPLE7fqFWnASagwiW5jWL6IEjnoGuDoCe # CH1aA6jpG2Sl5WSEOGEgZ8FiH5bxJ6Qx59DkNBtQ1MGAUyY0Bj2u/NHIPAMeeLSy # 68I3CXFXyc7vmftyC3g4gmCx5L7NLh6gAVTujHrb+uqMdxZwSFZh7zuo5uqrJSaV # gaw3DU+y7vmDASBFmY8ayr7QjvTXBUvlt+0PNmbXfq8CEWP5/HfYcLuJoqGo6njw # TOi1vKgvz4oU8dy3PbwKFhOLeqZ49zdLqzsEiricXPcrfO+dNvyvoMWy7Yz59CZ5 # 6zW27sHwIndrQOlw2fW4LLqCJzTkhGEQzWTSIA== # SIG # End signature block |