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")] [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" } "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" } "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" } "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" } "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" } "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 # MIIdhAYJKoZIhvcNAQcCoIIddTCCHXECAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUbZovn2ARp+LAvo55GP1ZcdqK # DF2gghhuMIIE3jCCA8agAwIBAgITMwAAAPWCNltOMNUpegAAAAAA9TANBgkqhkiG # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTgwODIzMjAyMDAx # WhcNMTkxMTIzMjAyMDAxWjCBzjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJp # Y28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjMxQzUtMzBCQS03QzkxMSUwIwYD # VQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIBIjANBgkqhkiG9w0B # AQEFAAOCAQ8AMIIBCgKCAQEAm/sn7nZknoTIZhhqV2OdPQgXJK60/vwvcGZ5mtE0 # zjMPmjgJSrO7rY4QEex6q5ABohJezl5KHkDUh++MqH/J4j/LHEVBi+tlxGplDAl+ # A3U8aJ4hBhJ9E22ngfqWUjaEwwl0TH4jTt2yrh01bSm8J2Zhva8fbXze4LEP9V8e # +4yt8141GjzDE6ydE8hHkacdT+OVEvYNcLpDLfzAca8v7QBDaA/nkW1x6l4gBTI0 # nrcICvNUEhOGQ92wFpayxGUM4PpUCU/zlgC9H1QIO2iSmAwW9txIVFZ6BAsHCrgJ # +7Lv/WZe3Hy/Auucqyxz7htTJ8UZzLAdOaUG+48vokN1VwIDAQABo4IBCTCCAQUw # HQYDVR0OBBYEFJdYHJtJesRExOaXuzglFyE78n+0MB8GA1UdIwQYMBaAFCM0+NlS # RnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly9jcmwubWlj # cm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY3Jvc29mdFRpbWVTdGFtcFBD # QS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsGAQUFBzAChjxodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcnQw # EwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQEFBQADggEBAG2yc+j3D03h # pVgguweQOvcuzdZQwToinE4pdTcXP2LNFC5qjCw1itnl+N2AUkdue0CiNqm1i9+o # fVmYI55g29wwG1aguuFV3fb/o7GGWWWpZzUXX0Yv3/fROMAY5GBY0g3JRC5FzAx7 # 5sO4uspbm0aB/iAT8Vd5grNzJZNfS9SMkirIYopWbBxfUHR5GgdrtV1anhhoYPPa # 1cnbh4Ac/GO3HmAUHKWjoWyEE4hBIjx02HAWuMKXrR7s0MaL2vowCAD9Y7og9WXl # Si/K2vOVBC+wBOEBXFtP4hoPN7cG//u53XwXSgomFQyDrKaqmrEiyERNy1F4D+4y # 9bkycbVYTaYwggX/MIID56ADAgECAhMzAAABA14lHJkfox64AAAAAAEDMA0GCSqG # SIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwHhcNMTgw # NzEyMjAwODQ4WhcNMTkwNzI2MjAwODQ4WjB0MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24w # ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRlHY25oarNv5p+UZ8i4hQ # y5Bwf7BVqSQdfjnnBZ8PrHuXss5zCvvUmyRcFrU53Rt+M2wR/Dsm85iqXVNrqsPs # E7jS789Xf8xly69NLjKxVitONAeJ/mkhvT5E+94SnYW/fHaGfXKxdpth5opkTEbO # ttU6jHeTd2chnLZaBl5HhvU80QnKDT3NsumhUHjRhIjiATwi/K+WCMxdmcDt66Va # mJL1yEBOanOv3uN0etNfRpe84mcod5mswQ4xFo8ADwH+S15UD8rEZT8K46NG2/Ys # AzoZvmgFFpzmfzS/p4eNZTkmyWPU78XdvSX+/Sj0NIZ5rCrVXzCRO+QUauuxygQj # AgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEEAYI3TAgBBggrBgEFBQcDAzAd # BgNVHQ4EFgQUR77Ay+GmP/1l1jjyA123r3f3QP8wUAYDVR0RBEkwR6RFMEMxKTAn # BgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMRYwFAYDVQQF # Ew0yMzAwMTIrNDM3OTY1MB8GA1UdIwQYMBaAFEhuZOVQBdOCqhc3NyK1bajKdQKV # MFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lv # cHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0wNy0wOC5jcmwwYQYIKwYBBQUH # AQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp # b3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0wNy0wOC5jcnQwDAYDVR0T # AQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAn/XJUw0/DSbsokTYDdGfY5YGSz8e # XMUzo6TDbK8fwAG662XsnjMQD6esW9S9kGEX5zHnwya0rPUn00iThoj+EjWRZCLR # ay07qCwVlCnSN5bmNf8MzsgGFhaeJLHiOfluDnjYDBu2KWAndjQkm925l3XLATut # ghIWIoCJFYS7mFAgsBcmhkmvzn1FFUM0ls+BXBgs1JPyZ6vic8g9o838Mh5gHOmw # GzD7LLsHLpaEk0UoVFzNlv2g24HYtjDKQ7HzSMCyRhxdXnYqWJ/U7vL0+khMtWGL # sIxB6aq4nZD0/2pCD7k+6Q7slPyNgLt44yOneFuybR/5WcF9ttE5yXnggxxgCto9 # sNHtNr9FB+kbNm7lPTsFA6fUpyUSj+Z2oxOzRVpDMYLa2ISuubAfdfX2HX1RETcn # 6LU1hHH3V6qu+olxyZjSnlpkdr6Mw30VapHxFPTy2TUxuNty+rR1yIibar+YRcdm # stf/zpKQdeTr5obSyBvbJ8BblW9Jb1hdaSreU0v46Mp79mwV+QMZDxGFqk+av6pX # 3WDG9XEg9FGomsrp0es0Rz11+iLsVT9qGTlrEOlaP470I3gwsvKmOMs1jaqYWSRA # uDpnpAdfoP7YO0kT+wzh7Qttg1DO8H8+4NkI6IwhSkHC3uuOW+4Dwx1ubuZUNWZn # cnwa6lL2IsRyP64wggYHMIID76ADAgECAgphFmg0AAAAAAAcMA0GCSqGSIb3DQEB # BQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJkiaJk/IsZAEZFgltaWNy # b3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhv # cml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0MDMxMzAzMDlaMHcxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAfBgNVBAMTGE1pY3Jvc29mdCBU # aW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ+h # bLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP7tGn0UytdDAgEesH1VSVFUmUG0KS # rphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySHnfL0Zxws/HvniB3q506jocEjU8qN # +kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUoRi4nrIZPVVIM5AMs+2qQkDBuh/NZ # MJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABKR2YRJylmqJfk0waBSqL5hKcRRxQJ # gp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSfrx54QTF3zJvfO4OToWECtR0Nsfz3 # m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGnMA8GA1UdEwEB/wQFMAMBAf8wHQYD # VR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMPMAsGA1UdDwQEAwIBhjAQBgkrBgEE # AYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQOrIJgQFYnl+UlE/wq4QpTlVnkpKFj # pGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcGCgmSJomT8ixkARkWCW1pY3Jv # c29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9y # aXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9j # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL21pY3Jvc29mdHJvb3Rj # ZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYBBQUHMAKGOGh0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9zb2Z0Um9vdENlcnQuY3J0MBMG # A1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBBQUAA4ICAQAQl4rDXANENt3p # tK132855UU0BsS50cVttDBOrzr57j7gu1BKijG1iuFcCy04gE1CZ3XpA4le7r1ia # HOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV3U+rkuTnjWrVgMHmlPIGL4UD6ZEq # JCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5nGctxVEO6mJcPxaYiyA/4gcaMvnM # MUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tOi3/FNSteo7/rvH0LQnvUU3Ih7jDK # u3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbMUVbonXCUbKw5TNT2eb+qGHpiKe+i # myk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXjpKh0NbhOxXEjEiZ2CzxSjHFaRkMU # vLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh0EPpK+m79EjMLNTYMoBMJipIJF9a # 6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLaxaj2JoXZhtG6hE6a/qkfwEm/9ijJs # sv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWwymO0eFQF1EEuUKyUsKV4q7OglnUa # 2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma7kng9wFlb4kLfchpyOZu6qeXzjEp # /w7FW1zYTRuh2Povnj8uVRZryROj/TCCB3owggVioAMCAQICCmEOkNIAAAAAAAMw # DQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhv # cml0eSAyMDExMB4XDTExMDcwODIwNTkwOVoXDTI2MDcwODIxMDkwOVowfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP # ADCCAgoCggIBAKvw+nIQHC6t2G6qghBNNLrytlghn0IbKmvpWlCquAY4GgRJun/D # DB7dN2vGEtgL8DjCmQawyDnVARQxQtOJDXlkh36UYCRsr55JnOloXtLfm1OyCizD # r9mpK656Ca/XllnKYBoF6WZ26DJSJhIv56sIUM+zRLdd2MQuA3WraPPLbfM6XKEW # 9Ea64DhkrG5kNXimoGMPLdNAk/jj3gcN1Vx5pUkp5w2+oBN3vpQ97/vjK1oQH01W # KKJ6cuASOrdJXtjt7UORg9l7snuGG9k+sYxd6IlPhBryoS9Z5JA7La4zWMW3Pv4y # 07MDPbGyr5I4ftKdgCz1TlaRITUlwzluZH9TupwPrRkjhMv0ugOGjfdf8NBSv4yU # h7zAIXQlXxgotswnKDglmDlKNs98sZKuHCOnqWbsYR9q4ShJnV+I4iVd0yFLPlLE # tVc/JAPw0XpbL9Uj43BdD1FGd7P4AOG8rAKCX9vAFbO9G9RVS+c5oQ/pI0m8GLhE # fEXkwcNyeuBy5yTfv0aZxe/CHFfbg43sTUkwp6uO3+xbn6/83bBm4sGXgXvt1u1L # 50kppxMopqd9Z4DmimJ4X7IvhNdXnFy/dygo8e1twyiPLI9AN0/B4YVEicQJTMXU # pUMvdJX3bvh4IFgsE11glZo+TzOE2rCIF96eTvSWsLxGoGyY0uDWiIwLAgMBAAGj # ggHtMIIB6TAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUSG5k5VAF04KqFzc3 # IrVtqMp1ApUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGG # MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h6qfHMdEj # iTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3Br # aS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNybDBe # BggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDCB # nwYDVR0gBIGXMIGUMIGRBgkrBgEEAYI3LgMwgYMwPwYIKwYBBQUHAgEWM2h0dHA6 # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvZG9jcy9wcmltYXJ5Y3BzLmh0bTBA # BggrBgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBwAG8AbABpAGMAeQBfAHMAdABh # AHQAZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAZ/KGpZjgVHkaLtPY # dGcimwuWEeFjkplCln3SeQyQwWVfLiw++MNy0W2D/r4/6ArKO79HqaPzadtjvyI1 # pZddZYSQfYtGUFXYDJJ80hpLHPM8QotS0LD9a+M+By4pm+Y9G6XUtR13lDni6WTJ # RD14eiPzE32mkHSDjfTLJgJGKsKKELukqQUMm+1o+mgulaAqPyprWEljHwlpblqY # luSD9MCP80Yr3vw70L01724lruWvJ+3Q3fMOr5kol5hNDj0L8giJ1h/DMhji8MUt # zluetEk5CsYKwsatruWy2dsViFFFWDgycScaf7H0J/jeLDogaZiyWYlobm+nt3TD # QAUGpgEqKD6CPxNNZgvAs0314Y9/HG8VfUWnduVAKmWjw11SYobDHWM2l4bf2vP4 # 8hahmifhzaWX0O5dY0HjWwechz4GdwbRBrF1HxS+YWG18NzGGwS+30HHDiju3mUv # 7Jf2oVyW2ADWoUa9WfOXpQlLSBCZgB/QACnFsZulP0V3HjXG0qKin3p6IvpIlR+r # +0cjgPWe+L9rt0uX4ut1eBrs6jeZeRhL/9azI2h15q/6/IvrC4DqaTuv/DDtBEyO # 3991bWORPdGdVk5Pv4BXIqF4ETIheu9BCrE/+6jMpF3BoYibV3FWTkhFwELJm3Zb # CoBIa/15n8G9bW1qyVJzEw16UM0xggSAMIIEfAIBATCBlTB+MQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29k # ZSBTaWduaW5nIFBDQSAyMDExAhMzAAABA14lHJkfox64AAAAAAEDMAkGBSsOAwIa # BQCggZQwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFIOKdBe38kCAhKx7XKJwkj6e # brg0MDQGCisGAQQBgjcCAQwxJjAkoBKAEABUAGUAcwB0AFMAaQBnAG6hDoAMaHR0 # cDovL3Rlc3QgMA0GCSqGSIb3DQEBAQUABIIBAJyD7slI3e+o/gU5V94uQaXai5Kz # AGHP/Fho0ab2DWBXx4slr0qjK61P5BIgrpSL89W7q3E62DmO+hUKDohTu4jPkED4 # bMc1IJ19iMKJS0r4E5sLFiLGUgFPyhikI2oQAbRH0YmgQ/IROjWbBScbmG8iAQ/x # eoUemRK0j0iFpAyy8C4f5odad0mcEEnn8RiWMAZ/r/7Qj2SKwXARc2FGKI3fsFhm # pHR9qnrveYBOrZ/baXDLTMNC7jCRW5P8Fsn2/h3dqEksXCIRPXuDPquhae8PDCcb # VT3TOKtbmYv1Uvm284FbwJFnb/X61iV0t7JioLTyVuq6xx552WPI+1NXSv+hggIo # MIICJAYJKoZIhvcNAQkGMYICFTCCAhECAQEwgY4wdzELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUtU3Rh # bXAgUENBAhMzAAAA9YI2W04w1Sl6AAAAAAD1MAkGBSsOAwIaBQCgXTAYBgkqhkiG # 9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xOTAxMDQyMzAwMjha # MCMGCSqGSIb3DQEJBDEWBBQCtmWo1rYqB/PxuxIXqgyiED/S4TANBgkqhkiG9w0B # AQUFAASCAQAI+iUtWi5s9qx1Y2WIkcJKu6oMve3oylNGfug/9HxCe07HWOuEFePt # swvyeAQ+W3FAyfQsHxcwqSJJmdDzN/NEBLTclF8iwgVPhFua/D9L/PW2XQME7ihA # 8yAx9mCjErIgDndm2YDSZ7HlbAwPA6MBaLvoUwmoDhPF0c6RaonRoqyWl9sLo6Pv # fP451ly/kWiwcuHTJkqJm3rf6WxbVnGV3PGghlknG3BdZ5PsGge348V0XgZK+OHw # WJqLizyo+cJEdt2+E8IJeE3HiB+yY5MzXKbt6VNFefd3xHTdhWNt3QaJDcECv07w # WbMnL2K3sESfGny2QcZC2VZrFKUt3D9S # SIG # End signature block |