tests/Connect-MsecGraphSdk.Tests.ps1
|
#Requires -Module Pester # # Tests for Connect-MsecGraphSdk. # # This is the bridge that lets the Microsoft.Graph SDK run as the msec app WITHOUT the # certificate's private key ever leaving Key Vault. What matters: # # * the token is handed over as a SecureString - the SDK's v2 signature # * the cloud comes from the msec session and is matched on the ENDPOINT, because the SDK # names clouds differently from Azure (China, not AzureChinaCloud) # * a token too close to expiry is refused UP FRONT. The SDK is given a static token and # cannot renew it, so a long report would start working and fail partway through - much # harder to diagnose than a refusal. $script:HasGraphSdk = $null -ne (Get-Module -ListAvailable Microsoft.Graph.Authentication) BeforeAll { $modulePath = Join-Path $PSScriptRoot '..' 'msec.psm1' Import-Module $modulePath -Force -ErrorAction Stop } AfterAll { Remove-Module Msec -Force -ErrorAction SilentlyContinue } Describe 'Connect-MsecGraphSdk' -Skip:(-not $script:HasGraphSdk) { BeforeEach { InModuleScope Msec { $script:MsecSession = @{ TenantId = 'tenant-1'; ClientId = 'client-1'; KeyVaultName = 'kv' KeyName = 'msec-app'; ThumbprintBytes = [byte[]](1..20) Endpoints = @{ GraphResource = 'https://graph.microsoft.com'; EnvironmentName = 'AzureCloud' } Tokens = @{} } } } It 'hands the session token to the SDK as a SecureString' { InModuleScope Msec { $script:MsecSession.Tokens['https://graph.microsoft.com'] = @{ Token = 'THE.SESSION.TOKEN'; ExpiresOn = [DateTimeOffset]::UtcNow.AddMinutes(55) } Mock Get-MsecAccessToken -MockWith { 'THE.SESSION.TOKEN' } Mock Connect-MgGraph -MockWith { } Connect-MsecGraphSdk Should -Invoke Connect-MgGraph -Times 1 -Exactly -ParameterFilter { # v2 of the SDK takes a SecureString, not a plain string. $AccessToken -is [System.Security.SecureString] -and [System.Net.NetworkCredential]::new('', $AccessToken).Password -eq 'THE.SESSION.TOKEN' -and $Environment -eq 'Global' } } } It 'matches the cloud on the endpoint, not on the Azure environment name' { InModuleScope Msec { # The Chinese cloud: Az calls it AzureChinaCloud, the SDK calls it China. Mapping # by name would need a lookup table that goes stale as clouds are added; the # endpoint is what actually has to agree. $script:MsecSession.Endpoints = @{ GraphResource = 'https://microsoftgraph.chinacloudapi.cn' EnvironmentName = 'AzureChinaCloud' } $script:MsecSession.Tokens['https://microsoftgraph.chinacloudapi.cn'] = @{ Token = 'cn'; ExpiresOn = [DateTimeOffset]::UtcNow.AddMinutes(55) } Mock Get-MsecAccessToken -MockWith { 'cn' } Mock Connect-MgGraph -MockWith { } Connect-MsecGraphSdk Should -Invoke Connect-MgGraph -Times 1 -Exactly -ParameterFilter { $Environment -eq 'China' } # ...and the token was requested for the sovereign endpoint, not the commercial one. Should -Invoke Get-MsecAccessToken -Times 1 -Exactly -ParameterFilter { $Resource -eq 'https://microsoftgraph.chinacloudapi.cn' } } } It 'refuses a token too close to expiry rather than failing mid-report' { InModuleScope Msec { $script:MsecSession.Tokens['https://graph.microsoft.com'] = @{ Token = 'nearly-done'; ExpiresOn = [DateTimeOffset]::UtcNow.AddMinutes(3) } Mock Get-MsecAccessToken -MockWith { 'nearly-done' } Mock Connect-MgGraph -MockWith { } # Three minutes left, thirty wanted: a long report must not start. { Connect-MsecGraphSdk -MinimumMinutes 30 } | Should -Throw '*cannot renew*' Should -Invoke Connect-MgGraph -Times 0 -Exactly # ...but the default is small enough that an ordinary call still goes through. Connect-MsecGraphSdk -MinimumMinutes 1 Should -Invoke Connect-MgGraph -Times 1 -Exactly } } It 'warns rather than silently signing in to the wrong cloud' { $warnings = @() InModuleScope Msec { # An endpoint no SDK environment knows: falling back to Global silently would # surface later as a wall of 401s that read as a permission problem. $script:MsecSession.Endpoints = @{ GraphResource = 'https://graph.example.invalid'; EnvironmentName = 'Custom' } $script:MsecSession.Tokens['https://graph.example.invalid'] = @{ Token = 'x'; ExpiresOn = [DateTimeOffset]::UtcNow.AddMinutes(55) } Mock Get-MsecAccessToken -MockWith { 'x' } Mock Connect-MgGraph -MockWith { } Connect-MsecGraphSdk } -WarningVariable warnings -WarningAction SilentlyContinue ($warnings -join ' ') | Should -Match 'probably wrong for this cloud' } It 'throws a clear error when not connected' { InModuleScope Msec { $script:MsecSession = $null { Connect-MsecGraphSdk } | Should -Throw '*Connect-Msec*' } } } |