-
Notifications
You must be signed in to change notification settings - Fork 4k
Force Get-AzAccessToken to always return SecureString as long as AsSecureString is set #27817
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,6 @@ | |
|
||
using Microsoft.Azure.Commands.Common.Authentication; | ||
using Microsoft.Azure.Commands.Common.Authentication.Abstractions; | ||
using Microsoft.Azure.Commands.Common.Authentication.Abstractions.Interfaces; | ||
using Microsoft.Azure.Commands.Common.Authentication.Models; | ||
using Microsoft.Azure.Commands.Profile; | ||
using Microsoft.Azure.Commands.Profile.Models; | ||
|
@@ -40,18 +39,14 @@ namespace Microsoft.Azure.Commands.ResourceManager.Common.Test | |
{ | ||
public class AccessTokenCmdletTests | ||
{ | ||
private GetAzureRmAccessTokenCommand cmdlet; | ||
private Mock<IAuthenticationFactory> factoryMock = new Mock<IAuthenticationFactory>(); | ||
private MockCommandRuntime mockedCommandRuntime; | ||
private IAuthenticationFactory previousFactory = null; | ||
|
||
private string tenantId = Guid.NewGuid().ToString(); | ||
|
||
public AccessTokenCmdletTests(ITestOutputHelper output) | ||
private GetAzureRmAccessTokenCommand CreateCommand() | ||
{ | ||
TestExecutionHelpers.SetUpSessionAndProfile(); | ||
XunitTracingInterceptor.AddToContext(new XunitTracingInterceptor(output)); | ||
AzureSession.Instance.RegisterComponent<AuthenticationTelemetry>(AuthenticationTelemetry.Name, () => new AuthenticationTelemetry()); | ||
var defaultContext = new AzureContext( | ||
new AzureSubscription() | ||
{ | ||
|
@@ -70,29 +65,40 @@ public AccessTokenCmdletTests(ITestOutputHelper output) | |
}); | ||
|
||
mockedCommandRuntime = new MockCommandRuntime(); | ||
cmdlet = new GetAzureRmAccessTokenCommand() | ||
var cmdlet = new GetAzureRmAccessTokenCommand() | ||
{ | ||
CommandRuntime = mockedCommandRuntime, | ||
DefaultProfile = new AzureRmProfile() | ||
}; | ||
cmdlet.DefaultProfile.DefaultContext = defaultContext; | ||
return cmdlet; | ||
} | ||
|
||
public AccessTokenCmdletTests(ITestOutputHelper output) | ||
{ | ||
TestExecutionHelpers.SetUpSessionAndProfile(); | ||
XunitTracingInterceptor.AddToContext(new XunitTracingInterceptor(output)); | ||
AzureSession.Instance.RegisterComponent<AuthenticationTelemetry>(AuthenticationTelemetry.Name, () => new AuthenticationTelemetry()); | ||
|
||
} | ||
|
||
[Fact] | ||
[Trait(Category.AcceptanceType, Category.CheckIn)] | ||
public void TestGetAccessTokenAsPlainText() | ||
{ | ||
// Setup | ||
var cmdlet = CreateCommand(); | ||
cmdlet.TenantId = tenantId; | ||
var fakeToken = "eyfaketoken.eyfaketoken"; | ||
Environment.SetEnvironmentVariable(Constants.AzPsOutputPlainTextAccessToken, bool.TrueString); | ||
|
||
var expected = new PSAccessToken { | ||
var expected = new PSAccessToken | ||
{ | ||
UserId = "[email protected]", | ||
TenantId = cmdlet.TenantId, | ||
Token = fakeToken | ||
}; | ||
|
||
factoryMock.Setup(t => t.Authenticate( | ||
It.IsAny<IAzureAccount>(), | ||
It.IsAny<IAzureEnvironment>(), | ||
|
@@ -132,6 +138,110 @@ public void TestGetAccessTokenAsPlainText() | |
public void TestGetAccessTokenAsSecureString() | ||
{ | ||
// Setup | ||
var cmdlet = CreateCommand(); | ||
cmdlet.TenantId = tenantId; | ||
var fakeToken = "eyfaketoken.eyfaketoken"; | ||
|
||
var expected = new PSSecureAccessToken(); | ||
expected.UserId = "[email protected]"; | ||
expected.TenantId = cmdlet.TenantId; | ||
expected.Token = fakeToken.ConvertToSecureString(); | ||
|
||
|
||
factoryMock.Setup(t => t.Authenticate( | ||
It.IsAny<IAzureAccount>(), | ||
It.IsAny<IAzureEnvironment>(), | ||
It.IsAny<string>(), | ||
It.IsAny<SecureString>(), | ||
It.IsAny<string>(), | ||
It.IsAny<Action<string>>(), | ||
It.IsAny<IDictionary<string, object>>())).Returns(new MockAccessToken | ||
{ | ||
UserId = expected.UserId, | ||
LoginType = LoginType.OrgId, | ||
AccessToken = fakeToken, | ||
TenantId = expected.TenantId | ||
}); | ||
previousFactory = AzureSession.Instance.AuthenticationFactory; | ||
AzureSession.Instance.AuthenticationFactory = factoryMock.Object; | ||
|
||
// Act | ||
cmdlet.InvokeBeginProcessing(); | ||
cmdlet.ExecuteCmdlet(); | ||
cmdlet.InvokeEndProcessing(); | ||
|
||
//Verify | ||
Assert.Single(mockedCommandRuntime.OutputPipeline); | ||
var outputPipeline = mockedCommandRuntime.OutputPipeline; | ||
Assert.Equal(expected.TenantId, ((PSSecureAccessToken)outputPipeline.First()).TenantId); | ||
Assert.Equal(expected.UserId, ((PSSecureAccessToken)outputPipeline.First()).UserId); | ||
Assert.Equal("Bearer", ((PSSecureAccessToken)outputPipeline.First()).Type); | ||
var expectedToken = expected.Token.ConvertToString(); | ||
var actualToken = ((PSSecureAccessToken)outputPipeline.First()).Token.ConvertToString(); | ||
Assert.Equal(expectedToken, actualToken); | ||
|
||
AzureSession.Instance.AuthenticationFactory = previousFactory; | ||
} | ||
|
||
[Fact] | ||
[Trait(Category.AcceptanceType, Category.CheckIn)] | ||
public void TestGetAccessTokenAsSecureStringWhenHasEnvVarAndAsSecureString() | ||
{ | ||
// Setup | ||
var cmdlet = CreateCommand(); | ||
cmdlet.TenantId = tenantId; | ||
cmdlet.AsSecureString = true; | ||
var fakeToken = "eyfaketoken.eyfaketoken"; | ||
Environment.SetEnvironmentVariable(Constants.AzPsOutputPlainTextAccessToken, bool.TrueString); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first test sets the plaintext env var but never clears it, causing leakage into subsequent tests. Consider resetting or scoping the environment variable (e.g., clear it at the start or end of each test). Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||
|
||
var expected = new PSSecureAccessToken(); | ||
expected.UserId = "[email protected]"; | ||
expected.TenantId = cmdlet.TenantId; | ||
expected.Token = fakeToken.ConvertToSecureString(); | ||
|
||
|
||
factoryMock.Setup(t => t.Authenticate( | ||
It.IsAny<IAzureAccount>(), | ||
It.IsAny<IAzureEnvironment>(), | ||
It.IsAny<string>(), | ||
It.IsAny<SecureString>(), | ||
It.IsAny<string>(), | ||
It.IsAny<Action<string>>(), | ||
It.IsAny<IDictionary<string, object>>())).Returns(new MockAccessToken | ||
{ | ||
UserId = expected.UserId, | ||
LoginType = LoginType.OrgId, | ||
AccessToken = fakeToken, | ||
TenantId = expected.TenantId | ||
}); | ||
previousFactory = AzureSession.Instance.AuthenticationFactory; | ||
AzureSession.Instance.AuthenticationFactory = factoryMock.Object; | ||
|
||
// Act | ||
cmdlet.InvokeBeginProcessing(); | ||
cmdlet.ExecuteCmdlet(); | ||
cmdlet.InvokeEndProcessing(); | ||
|
||
//Verify | ||
Assert.Single(mockedCommandRuntime.OutputPipeline); | ||
var outputPipeline = mockedCommandRuntime.OutputPipeline; | ||
Assert.Equal(expected.TenantId, ((PSSecureAccessToken)outputPipeline.First()).TenantId); | ||
Assert.Equal(expected.UserId, ((PSSecureAccessToken)outputPipeline.First()).UserId); | ||
Assert.Equal("Bearer", ((PSSecureAccessToken)outputPipeline.First()).Type); | ||
var expectedToken = expected.Token.ConvertToString(); | ||
var actualToken = ((PSSecureAccessToken)outputPipeline.First()).Token.ConvertToString(); | ||
Assert.Equal(expectedToken, actualToken); | ||
|
||
Environment.SetEnvironmentVariable(Constants.AzPsOutputPlainTextAccessToken, null); | ||
AzureSession.Instance.AuthenticationFactory = previousFactory; | ||
} | ||
|
||
[Fact] | ||
[Trait(Category.AcceptanceType, Category.CheckIn)] | ||
public void TestGetAccessTokenAsSecureStringWhenHasSecureString() | ||
{ | ||
// Setup | ||
var cmdlet = CreateCommand(); | ||
cmdlet.TenantId = tenantId; | ||
cmdlet.AsSecureString = true; | ||
var fakeToken = "eyfaketoken.eyfaketoken"; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -156,7 +156,7 @@ public override void ExecuteCmdlet() | |
//Throw exception when the caller doesn't have permission. | ||
//Use SecureString only when AZUREPS_OUTPUT_PLAINTEXT_AZACCESSTOKEN is successfully set. | ||
} | ||
if (usePlainText) | ||
if (usePlainText && !AsSecureString) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] Consider adding an inline comment explaining that the plain text output is intentionally bypassed when AsSecureString is set. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||
{ | ||
WriteObject(result); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider wrapping the assignment and restoration of AzureSession.Instance.AuthenticationFactory in a try–finally block to ensure test isolation even if exceptions occur.
Copilot uses AI. Check for mistakes.