Skip to content

Commit

Permalink
Fixed Azure tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rmbolger committed Jun 28, 2023
1 parent f854257 commit d9af065
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 104 deletions.
6 changes: 5 additions & 1 deletion Posh-ACME/Plugins/Azure.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,10 @@ function ConvertFrom-AccessToken {
# Anatomy of an access token
# https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-token-and-claims

# In this function we're extracting the tenant ID and expiration value from
# the claims in the payload and returning an object that includes them along
# with the original token value.

# grab the payload section of the JWT
$null,$payload,$null = $AZAccessToken.Split('.')

Expand All @@ -377,7 +381,7 @@ function ConvertFrom-AccessToken {
# make sure the token hasn't expired
$expires = [DateTimeOffset]::FromUnixTimeSeconds($claims.exp)
if ((Get-DateTimeOffsetNow) -gt $expires) {
throw "The provided access token has expired as of $($expires.ToString('u'))"
throw "The provided access token expired since $($expires.ToString('u'))"
}

# return an object that contains the 'expires_on' property along with the token
Expand Down
208 changes: 105 additions & 103 deletions Tests/Azure.Tests.ps1
Original file line number Diff line number Diff line change
@@ -1,157 +1,159 @@
Describe "Connect-AZTenant" {

BeforeAll {

. (Join-Path $PSScriptRoot "..\Posh-ACME\Plugins\Azure.ps1")
. (Join-Path $PSScriptRoot "..\Posh-ACME\Private\MockWrappers.ps1")
. (Join-Path $PSScriptRoot "..\Posh-ACME\Private\ConvertTo-Base64Url.ps1")
$script:UseBasic = @{UseBasicParsing = $true}

$fakeTokenText = [ordered]@{
aud = 'https://management.core.windows.net/'
exp = [DateTimeOffset]::Parse('2018-07-04T10:00:00Z').ToUnixTimeSeconds() # 1530698400
tid = '00000000-0000-0000-0000-000000000000'
} | ConvertTo-Json | ConvertTo-Base64Url

$fakeTokenResponse = [pscustomobject]@{
expires_on = '1530691200' # 2018-07-04 08:00:00 UTC
access_token = 'faketoken'
expires_on = [DateTimeOffset]::Parse('2018-07-04T10:00:00Z').ToUnixTimeSeconds() # 1530698400
access_token = $fakeTokenText
tenant = '00000000-0000-0000-0000-000000000000'
}

$script:UseBasic = @{UseBasicParsing = $true}

Mock Invoke-RestMethod { return $fakeTokenResponse }
Mock ConvertFrom-AccessToken { return $fakeTokenResponse }
Mock Get-DateTimeOffsetNow { return [DateTimeOffset]::Parse('2018-07-04T09:00:00Z') }

$fakeGoodToken = [pscustomobject]@{
# just after mocked "Now"
Expires = [DateTimeOffset]::Parse('2018-07-04T09:05:00Z')
AuthHeader = @{ Authorization = 'Bearer fakegoodtoken' }
}

$fakeExpiredToken = [pscustomobject]@{
# just before mocked "Now"
Expires = [DateTimeOffset]::Parse('2018-07-04T08:55:00Z')
AuthHeader = @{ Authorization = 'Bearer fakeexpiredtoken' }
}
}

Context "Credential param set" {
Context "Token param set" {

BeforeAll {
$fakeTenant = '00000000-0000-0000-0000-000000000000'
$fakePass = "fake+p&ss" | ConvertTo-SecureString -AsPlainText -Force
$fakeCred = New-Object System.Management.Automation.PSCredential('fake user', $fakePass)
$fakeAccessToken = "blah.$fakeTokenText.blah"
$mockedBearer = "Bearer $fakeTokenText"
}

It "calls Invoke-RestMethod if no existing token" {
$script:AZToken = $null
Connect-AZTenant -AZTenantId $fakeTenant -AZAppCred $fakeCred
Should -Invoke Invoke-RestMethod -Times 1 -Exactly -Scope It -ParameterFilter {
$Body -match "[&?]client_id=fake%20user(&|$)" -and $Body -match "[&?]client_secret=fake%2[Bb]p%26ss(&|$)"
}
Should -Invoke ConvertFrom-AccessToken -Times 0 -Exactly -Scope It
}

It "calls Invoke-RestMethod if token expired" {
$script:AZToken = $fakeExpiredToken
Connect-AZTenant -AZTenantId $fakeTenant -AZAppCred $fakeCred
Should -Invoke Invoke-RestMethod -Times 1 -Exactly -Scope It
Should -Invoke ConvertFrom-AccessToken -Times 0 -Exactly -Scope It
}
It "Uses the token as-is" -TestCases @(
@{ curToken = $null }
@{ curToken = [pscustomobject]@{
Expires = [DateTimeOffset]::Parse('2018-07-04T06:00:00Z') # expired before mocked "Now"
AuthHeader = @{ Authorization = 'Bearer fakeexpiredtoken' }
Tenant = '11111111-1111-1111-1111-111111111111'
}}
) {
# setup an expired cached token
$script:AZToken = $curToken

It "sets new AZToken if token expired" {
$script:AZToken | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader.Authorization | Should -BeExactly 'Bearer faketoken'
$script:AZToken.Expires | Should -Be ([DateTimeOffset]::Parse('2018-07-04T07:55:00Z'))
}
Connect-AZTenant -AZAccessToken $fakeAccessToken

It "calls nothing if current token is valid" {
$script:AZToken = $fakeGoodToken
Connect-AZTenant -AZTenantId $fakeTenant -AZAppCred $fakeCred
Should -Invoke Invoke-RestMethod -Times 0 -Exactly -Scope It
Should -Invoke ConvertFrom-AccessToken -Times 0 -Exactly -Scope It
}
Should -Invoke ConvertFrom-AccessToken -Times 1 -Exactly -Scope It

It "does not overwrite existing token if current token is valid" {
$script:AZToken | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader.Authorization | Should -BeExactly $fakeGoodToken.AuthHeader.Authorization
$script:AZToken.Expires | Should -Be $fakeGoodToken.Expires
$script:AZToken.Tenant | Should -Be '00000000-0000-0000-0000-000000000000'
$script:AZToken.Expires | Should -Be ([DateTimeOffset]::Parse('2018-07-04T09:55:00Z')) # 5 min prior to actual
$script:AZToken.AuthHeader.Authorization | Should -BeExactly $mockedBearer
}
}

Context "Token param set" {
Context "IMDS param set" {

BeforeAll {
$fakeAccessToken = 'blah.blah.blah'
$mockedBearer = "Bearer $fakeTokenText"
}

It "calls ConvertFrom-AccessToken if no existing token" {
$script:AZToken = $null
Connect-AZTenant -AZAccessToken $fakeAccessToken
Should -Invoke Invoke-RestMethod -Times 0 -Exactly -Scope It
Should -Invoke ConvertFrom-AccessToken -Times 1 -Exactly -Scope It
}
It "Gets new token if no current or expired" -TestCases @(
@{ curToken = $null }
@{ curToken = [pscustomobject]@{
Expires = [DateTimeOffset]::Parse('2018-07-04T06:00:00Z')
AuthHeader = @{ Authorization = 'Bearer fakeexpiredtoken' }
Tenant = '00000000-0000-0000-0000-000000000000'
}}
) {
$script:AZToken = $curToken

It "calls ConvertFrom-AccessToken if token expired" {
$script:AZToken = $fakeExpiredToken
Connect-AZTenant -AZAccessToken $fakeAccessToken
Should -Invoke Invoke-RestMethod -Times 0 -Exactly -Scope It
Connect-AZTenant -AZUseIMDS

Should -Invoke Invoke-RestMethod -Times 1 -Exactly -Scope It -ParameterFilter {
$Uri -eq 'http://169.254.169.254/metadata/identity/oauth2/token'
}
Should -Invoke ConvertFrom-AccessToken -Times 1 -Exactly -Scope It
}

It "uses passed in token if AZToken expired" {
$script:AZToken | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader.Authorization | Should -BeExactly 'Bearer faketoken'
$script:AZToken.Expires | Should -Be ([DateTimeOffset]::Parse('2018-07-04T07:55:00Z'))
$script:AZToken.Tenant | Should -Be '00000000-0000-0000-0000-000000000000'
$script:AZToken.Expires | Should -Be ([DateTimeOffset]::Parse('2018-07-04T09:55:00Z')) # 5 min prior to actual
$script:AZToken.AuthHeader.Authorization | Should -BeExactly $mockedBearer
}

It "calls nothing if current token is valid" {
$script:AZToken = $fakeGoodToken
Connect-AZTenant -AZAccessToken $fakeAccessToken
It "Uses existing token if still valid" {
$script:AZToken = [pscustomobject]@{
Expires = [DateTimeOffset]::Parse('2018-07-04T10:00:00Z')
AuthHeader = @{ Authorization = 'Bearer fakeexpiredtoken' }
Tenant = '00000000-0000-0000-0000-000000000000'
}

Connect-AZTenant -AZUseIMDS

Should -Invoke Invoke-RestMethod -Times 0 -Exactly -Scope It
Should -Invoke ConvertFrom-AccessToken -Times 0 -Exactly -Scope It
}

It "does not overwrite existing token if current token is valid" {
$script:AZToken | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader.Authorization | Should -BeExactly $fakeGoodToken.AuthHeader.Authorization
$script:AZToken.Expires | Should -Be $fakeGoodToken.Expires
$script:AZToken.Tenant | Should -Be '00000000-0000-0000-0000-000000000000'
$script:AZToken.Expires | Should -Be ([DateTimeOffset]::Parse('2018-07-04T10:00:00Z'))
$script:AZToken.AuthHeader.Authorization | Should -BeExactly 'Bearer fakeexpiredtoken'
}

}

Context "IMDS param set" {
Context "Credential param set" {

It "calls Invoke-RestMethod if no existing token" {
$script:AZToken = $null
Connect-AZTenant -AZUseIMDS
Should -Invoke Invoke-RestMethod -Times 1 -Exactly -Scope It
Should -Invoke ConvertFrom-AccessToken -Times 0 -Exactly -Scope It
BeforeAll {
$mockedBearer = "Bearer $fakeTokenText"
$fakeTenant = '00000000-0000-0000-0000-000000000000'
$fakePass = "fake+p&ss" | ConvertTo-SecureString -AsPlainText -Force
$fakeCred = New-Object System.Management.Automation.PSCredential('fake user', $fakePass)
}

It "calls Invoke-RestMethod if token expired" {
$script:AZToken = $fakeExpiredToken
Connect-AZTenant -AZUseIMDS
Should -Invoke Invoke-RestMethod -Times 1 -Exactly -Scope It
Should -Invoke ConvertFrom-AccessToken -Times 0 -Exactly -Scope It
}
It "Gets new token if no current, expired current, or new tenant" -TestCases @(
@{ curToken = $null }
@{ curToken = [pscustomobject]@{
Expires = [DateTimeOffset]::Parse('2018-07-04T06:00:00Z')
AuthHeader = @{ Authorization = 'Bearer fakeexpiredtoken' }
Tenant = '00000000-0000-0000-0000-000000000000'
}}
@{ curToken = [pscustomobject]@{
Expires = [DateTimeOffset]::Parse('2018-07-04T10:00:00Z')
AuthHeader = @{ Authorization = 'Bearer fakeexpiredtoken' }
Tenant = '11111111-1111-1111-1111-111111111111'
}}
) {
$script:AZToken = $null

It "sets new AZToken if token expired" {
$script:AZToken | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader.Authorization | Should -BeExactly 'Bearer faketoken'
$script:AZToken.Expires | Should -Be ([DateTimeOffset]::Parse('2018-07-04T07:55:00Z'))
Connect-AZTenant -AZTenantId $fakeTenant -AZAppCred $fakeCred

Should -Invoke Invoke-RestMethod -Times 1 -Exactly -Scope It -ParameterFilter {
$Body -match "[&?]client_id=fake%20user(&|$)" -and $Body -match "[&?]client_secret=fake%2[Bb]p%26ss(&|$)"
}
Should -Invoke ConvertFrom-AccessToken -Times 1 -Exactly -Scope It

$script:AZToken.Tenant | Should -Be '00000000-0000-0000-0000-000000000000'
$script:AZToken.Expires | Should -Be ([DateTimeOffset]::Parse('2018-07-04T09:55:00Z')) # 5 min prior to actual
$script:AZToken.AuthHeader.Authorization | Should -BeExactly $mockedBearer
}

It "calls nothing if current token is valid" {
$script:AZToken = $fakeGoodToken
Connect-AZTenant -AZUseIMDS
It "Uses existing token if still valid" {
$script:AZToken = [pscustomobject]@{
Expires = [DateTimeOffset]::Parse('2018-07-04T10:00:00Z')
AuthHeader = @{ Authorization = 'Bearer fakeexpiredtoken' }
Tenant = '00000000-0000-0000-0000-000000000000'
}

Connect-AZTenant -AZTenantId $fakeTenant -AZAppCred $fakeCred

Should -Invoke Invoke-RestMethod -Times 0 -Exactly -Scope It
Should -Invoke ConvertFrom-AccessToken -Times 0 -Exactly -Scope It
}

It "does not overwrite existing token if current token is valid" {
$script:AZToken | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader | Should -Not -BeNullOrEmpty
$script:AZToken.AuthHeader.Authorization | Should -BeExactly $fakeGoodToken.AuthHeader.Authorization
$script:AZToken.Expires | Should -Be $fakeGoodToken.Expires
$script:AZToken.Tenant | Should -Be '00000000-0000-0000-0000-000000000000'
$script:AZToken.Expires | Should -Be ([DateTimeOffset]::Parse('2018-07-04T10:00:00Z'))
$script:AZToken.AuthHeader.Authorization | Should -BeExactly 'Bearer fakeexpiredtoken'
}

}

}

0 comments on commit d9af065

Please sign in to comment.