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")] [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:11777" } "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:11777" } "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:11777" } "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" } "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:11777" } "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 # MIIdgAYJKoZIhvcNAQcCoIIdcTCCHW0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUJrch84DHyrWA02Gck4lVCcdr # gPKgghhqMIIE2jCCA8KgAwIBAgITMwAAAQNJnHZC8Tc9nQAAAAABAzANBgkqhkiG # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTgwODIzMjAyMDIz # WhcNMTkxMTIzMjAyMDIzWjCByjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # LTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEm # MCQGA1UECxMdVGhhbGVzIFRTUyBFU046MDg0Mi00QkU2LUMyOUExJTAjBgNVBAMT # HE1pY3Jvc29mdCBUaW1lLVN0YW1wIHNlcnZpY2UwggEiMA0GCSqGSIb3DQEBAQUA # A4IBDwAwggEKAoIBAQDByFwlHc/m05+num0WecWK+Ex6+nUpVZUOBSYinELdC+89 # nDecvRv6YOXRSRQVnONk59D/zeQy4aptYHogIN5oP03ZTsbtFtRNoXkp2QS4+uLa # s05wRgQJ3bi8wWdCWjanG/ze0zyrYdTeEiLtWKae/zB0js6VevTfuqym8/nrCvRB # otZmC+DRcOyLNeyMUQSWKiuO/tuLVG99ivZZkOY5CAVmIad8X7io64tYLh6hGYRx # TJzFlRBG/dB2fyZgHMdWC4uS4WcngI48WcL4K4lbaxMSRjNBoR5FA/73dnlQX+Xk # 8hPUkN6ZNgqiV36n5a4Q5UsrrA4AY05+1aNZq7zLAgMBAAGjggEJMIIBBTAdBgNV # HQ4EFgQUkLbf4SWYMx88WRVVKkwq0AqQYQowHwYDVR0jBBgwFoAUIzT42VJGcArt # QPt2+7MrsMM1sw8wVAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL2NybC5taWNyb3Nv # ZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljcm9zb2Z0VGltZVN0YW1wUENBLmNy # bDBYBggrBgEFBQcBAQRMMEowSAYIKwYBBQUHMAKGPGh0dHA6Ly93d3cubWljcm9z # b2Z0LmNvbS9wa2kvY2VydHMvTWljcm9zb2Z0VGltZVN0YW1wUENBLmNydDATBgNV # HSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQUFAAOCAQEAjHJC/jR5vKPN8pHg # QFnLzbgnLPV3d+/74KDKh3/gvl6m3uOmbKP5/PvO5LEijkgcMOVu09d9OqDvEfHF # kBDAs71ZQdvw/vb30BqNBe0z+ky/MFG982FWAqKmPjMuEI8sAKsWaNa7mn9IMWHh # vblEZprxu4BoxRHunxm2I9VsC35Bo2aC/GpYreUYHqM6zUxeo4fO+xA3RsZaM0Qs # kd5lD1ypJxj6urjbafEoATGpVCKa/s6jWUxNLtxwubdlzEb+5S5vo0C22PpAB6vB # TgaeREDOEVXUU6N1bkNxfQeXCfzfnICsfELbR4DXVKex5ZKM862hszR+DhWa9F/D # DVgg3zCCBf8wggPnoAMCAQICEzMAAAEDXiUcmR+jHrgAAAAAAQMwDQYJKoZIhvcN # AQELBQAwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYG # A1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMTAeFw0xODA3MTIy # MDA4NDhaFw0xOTA3MjYyMDA4NDhaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xHjAcBgNVBAMTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANGUdjbmhqs2/mn5RnyLiFDLkHB/ # sFWpJB1+OecFnw+se5eyznMK+9SbJFwWtTndG34zbBH8OybzmKpdU2uqw+wTuNLv # z1d/zGXLr00uMrFWK040B4n+aSG9PkT73hKdhb98doZ9crF2m2HmimRMRs621TqM # d5N3ZyGctloGXkeG9TzRCcoNPc2y6aFQeNGEiOIBPCL8r5YIzF2ZwO3rpVqYkvXI # QE5qc6/e43R6019Gl7ziZyh3mazBDjEWjwAPAf5LXlQPysRlPwrjo0bb9iwDOhm+ # aAUWnOZ/NL+nh41lOSbJY9Tvxd29Jf79KPQ0hnmsKtVfMJE75BRq67HKBCMCAwEA # AaOCAX4wggF6MB8GA1UdJQQYMBYGCisGAQQBgjdMCAEGCCsGAQUFBwMDMB0GA1Ud # DgQWBBRHvsDL4aY//WXWOPIDXbevd/dA/zBQBgNVHREESTBHpEUwQzEpMCcGA1UE # CxMgTWljcm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xFjAUBgNVBAUTDTIz # MDAxMis0Mzc5NjUwHwYDVR0jBBgwFoAUSG5k5VAF04KqFzc3IrVtqMp1ApUwVAYD # VR0fBE0wSzBJoEegRYZDaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j # cmwvTWljQ29kU2lnUENBMjAxMV8yMDExLTA3LTA4LmNybDBhBggrBgEFBQcBAQRV # MFMwUQYIKwYBBQUHMAKGRWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y2VydHMvTWljQ29kU2lnUENBMjAxMV8yMDExLTA3LTA4LmNydDAMBgNVHRMBAf8E # AjAAMA0GCSqGSIb3DQEBCwUAA4ICAQCf9clTDT8NJuyiRNgN0Z9jlgZLPx5cxTOj # pMNsrx/AAbrrZeyeMxAPp6xb1L2QYRfnMefDJrSs9SfTSJOGiP4SNZFkItFrLTuo # LBWUKdI3luY1/wzOyAYWFp4kseI5+W4OeNgMG7YpYCd2NCSb3bmXdcsBO62CEhYi # gIkVhLuYUCCwFyaGSa/OfUUVQzSWz4FcGCzUk/Jnq+JzyD2jzfwyHmAc6bAbMPss # uwculoSTRShUXM2W/aDbgdi2MMpDsfNIwLJGHF1edipYn9Tu8vT6SEy1YYuwjEHp # qridkPT/akIPuT7pDuyU/I2Au3jjI6d4W7JtH/lZwX220TnJeeCDHGAK2j2w0e02 # v0UH6Rs2buU9OwUDp9SnJRKP5najE7NFWkMxgtrYhK65sB919fYdfVERNyfotTWE # cfdXqq76iXHJmNKeWmR2vozDfRVqkfEU9PLZNTG423L6tHXIiJtqv5hFx2ay1//O # kpB15OvmhtLIG9snwFuVb0lvWF1pKt5TS/joynv2bBX5AxkPEYWqT5q/qlfdYMb1 # cSD0UaiayunR6zRHPXX6IuxVP2oZOWsQ6Vo/jvQjeDCy8qY4yzWNqphZJEC4Omek # B1+g/tg7SRP7DOHtC22DUM7wfz7g2QjojCFKQcLe645b7gPDHW5u5lQ1ZmdyfBrq # UvYixHI/rjCCBgcwggPvoAMCAQICCmEWaDQAAAAAABwwDQYJKoZIhvcNAQEFBQAw # XzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcGCgmSJomT8ixkARkWCW1pY3Jvc29m # dDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # MB4XDTA3MDQwMzEyNTMwOVoXDTIxMDQwMzEzMDMwOVowdzELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn6Fssd/b # SJIqfGsuGeG94uPFmVEjUK3O3RhOJA/u0afRTK10MCAR6wfVVJUVSZQbQpKumFww # JtoAa+h7veyJBw/3DgSY8InMH8szJIed8vRnHCz8e+eIHernTqOhwSNTyo36Rc8J # 0F6v0LBCBKL5pmyTZ9co3EZTsIbQ5ShGLieshk9VUgzkAyz7apCQMG6H81kwnfp+ # 1pez6CGXfvjSE/MIt1NtUrRFkJ9IAEpHZhEnKWaol+TTBoFKovmEpxFHFAmCn4Tt # VXj+AZodUAiFABAwRu233iNGu8QtVJ+vHnhBMXfMm987g5OhYQK1HQ2x/PebsgHO # IktU//kFw8IgCwIDAQABo4IBqzCCAacwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E # FgQUIzT42VJGcArtQPt2+7MrsMM1sw8wCwYDVR0PBAQDAgGGMBAGCSsGAQQBgjcV # AQQDAgEAMIGYBgNVHSMEgZAwgY2AFA6sgmBAVieX5SUT/CrhClOVWeSkoWOkYTBf # MRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0 # MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmC # EHmtFqFKoKWtTHNY9AcTLmUwUAYDVR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC5t # aWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvbWljcm9zb2Z0cm9vdGNlcnQu # Y3JsMFQGCCsGAQUFBwEBBEgwRjBEBggrBgEFBQcwAoY4aHR0cDovL3d3dy5taWNy # b3NvZnQuY29tL3BraS9jZXJ0cy9NaWNyb3NvZnRSb290Q2VydC5jcnQwEwYDVR0l # BAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQEFBQADggIBABCXisNcA0Q23em0rXfb # znlRTQGxLnRxW20ME6vOvnuPuC7UEqKMbWK4VwLLTiATUJndekDiV7uvWJoc4R0B # hqy7ePKL0Ow7Ae7ivo8KBciNSOLwUxXdT6uS5OeNatWAweaU8gYvhQPpkSokInD7 # 9vzkeJkuDfcH4nC8GE6djmsKcpW4oTmcZy3FUQ7qYlw/FpiLID/iBxoy+cwxSnYx # PStyC8jqcD3/hQoT38IKYY7w17gX606Lf8U1K16jv+u8fQtCe9RTciHuMMq7eGVc # WwEXChQO0toUmPU8uWZYsy0v5/mFhsxRVuidcJRsrDlM1PZ5v6oYemIp76KbKTQG # dxpiyT0ebR+C8AvHLLvPQ7Pl+ex9teOkqHQ1uE7FcSMSJnYLPFKMcVpGQxS8s7Ow # TWfIn0L/gHkhgJ4VMGboQhJeGsieIiHQQ+kr6bv0SMws1NgygEwmKkgkX1rqVu+m # 3pmdyjpvvYEndAYR7nYhv5uCwSdUtrFqPYmhdmG0bqETpr+qR/ASb/2KMmyy/t9R # yIwjyWa9nR2HEmQCPS2vWY+45CHltbDKY7R4VAXUQS5QrJSwpXirs6CWdRrZkocT # dSIvMqgIbqBbjCW/oO+EyiHW6x5PyZruSeD3AWVviQt9yGnI5m7qp5fOMSn/DsVb # XNhNG6HY+i+ePy5VFmvJE6P9MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCBIAwggR8AgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAAEDXiUcmR+jHrgAAAAAAQMwCQYFKw4DAhoFAKCB # lDAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYK # KwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUVGwk6E2/Jbu3SUQLpj0U8jOR34ow # NAYKKwYBBAGCNwIBDDEmMCSgEoAQAFQAZQBzAHQAUwBpAGcAbqEOgAxodHRwOi8v # dGVzdCAwDQYJKoZIhvcNAQEBBQAEggEAhpOEMFcPaDrOhqhI/VwPMsAQcoxbfZOT # 6lYrC5ZHgnqxz1bPs8utO4JHofl4iE+Pr0zHsxXkrkQt9vKsOLgVRA99652NhiQI # eFCyQFyT8O67G5GnlqkYmZjSd/O+lM5OrQrqhbzleJ02nRQJsstWCRaLRrnrIIzH # adKDamM8bx6+fArOO8S3Uxskf1qXQmEMPHddzwvWIWLUyJYUopKczwV7bIsxbD56 # NO+QU5l0j9MtWChQlC8hWnHdWdSAUURsqd8R4+nAXBrojsW60ir0uAORfccPRprS # MQssU1lJO9ZZHNHWUPbEAdtmVd8CaiODWEyYklyctOxdfDqdEj/N2qGCAigwggIk # BgkqhkiG9w0BCQYxggIVMIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQ # Q0ECEzMAAAEDSZx2QvE3PZ0AAAAAAQMwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJ # AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE5MDUwNzE4NDc0OFowIwYJ # KoZIhvcNAQkEMRYEFMh9Sxf+M8FRmOgHyyStu5scclsLMA0GCSqGSIb3DQEBBQUA # BIIBABFr55lrOkxVlsdiYXyJmFKE7MufySAAEUfJcLAeZKypj9PM49CrjE+FHXLF # r996Cj6Nx5LLZG5jPjnnjuupZ9BNocDxwKx8UQ04ndpLD4NOHdfLKQ4q/AWXYBTH # CRT0/XpjGO0Sfnrr7xDjd7LwIjTCCJCH3HrGyS5XnR1g7Pwult3IKBs//955i+0a # O8kbeqPSDH8OJtrWxInW2nlISNBVk9EZZFoevRi34gEuvZnkCmpgdJ/LR/fg7QaJ # jcf2MMoC6pfh1cPkXT1rf76qD5zzg9ox7O3PBSGfxqlXZy/ov6DLooZW76yuDF3O # 2PwUk9edKXL2HQzZPohzOb0TLtM= # SIG # End signature block |