Skip to content

Commit 9038994

Browse files
msJinLeividai-msft
andauthored
Add AsSecureString to Get-AzAccessToken to convert the returned token to SecureString (#24355)
* Add AsSecureString to Get-AzAccessToken to convert the returned token to SecureString Fix #24190 * Update src/Accounts/Accounts/ChangeLog.md Co-authored-by: Vincent Dai <23257217+vidai-msft@users.noreply.github.com> * Update src/Accounts/Accounts/Token/GetAzureRmAccessToken.cs Co-authored-by: Vincent Dai <23257217+vidai-msft@users.noreply.github.com> * Address review comments --------- Co-authored-by: Vincent Dai <23257217+vidai-msft@users.noreply.github.com>
1 parent a6344eb commit 9038994

13 files changed

+298
-21
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using Microsoft.Azure.Commands.Common.Authentication;
16+
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
17+
using Microsoft.Azure.Commands.Common.Authentication.Models;
18+
using Microsoft.Azure.Commands.Profile;
19+
using Microsoft.Azure.Commands.Profile.Models;
20+
using Microsoft.Azure.Commands.ScenarioTest;
21+
using Microsoft.Azure.Commands.TestFx.Mocks;
22+
using Microsoft.Azure.ServiceManagement.Common.Models;
23+
using Microsoft.WindowsAzure.Commands.Common;
24+
using Microsoft.WindowsAzure.Commands.Common.Test.Mocks;
25+
using Microsoft.WindowsAzure.Commands.ScenarioTest;
26+
using Microsoft.WindowsAzure.Commands.Utilities.Common;
27+
28+
using Moq;
29+
30+
using System;
31+
using System.Linq;
32+
using System.Security;
33+
34+
using Xunit;
35+
using Xunit.Abstractions;
36+
37+
namespace Microsoft.Azure.Commands.ResourceManager.Common.Test
38+
{
39+
public class AccessTokenCmdletTests
40+
{
41+
private GetAzureRmAccessTokenCommand cmdlet;
42+
private Mock<IAuthenticationFactory> factoryMock = new Mock<IAuthenticationFactory>();
43+
private MockCommandRuntime mockedCommandRuntime;
44+
private IAuthenticationFactory previousFactory = null;
45+
46+
private string tenantId = Guid.NewGuid().ToString();
47+
48+
public AccessTokenCmdletTests(ITestOutputHelper output)
49+
{
50+
TestExecutionHelpers.SetUpSessionAndProfile();
51+
XunitTracingInterceptor.AddToContext(new XunitTracingInterceptor(output));
52+
53+
var defaultContext = new AzureContext(
54+
new AzureSubscription()
55+
{
56+
Id = Guid.NewGuid().ToString(),
57+
Name = "Test subscription"
58+
},
59+
new AzureAccount()
60+
{
61+
Id = "admin@contoso.com",
62+
Type = AzureAccount.AccountType.User
63+
},
64+
AzureEnvironment.PublicEnvironments[EnvironmentName.AzureCloud],
65+
new AzureTenant()
66+
{
67+
Id = tenantId
68+
});
69+
70+
mockedCommandRuntime = new MockCommandRuntime();
71+
cmdlet = new GetAzureRmAccessTokenCommand()
72+
{
73+
CommandRuntime = mockedCommandRuntime,
74+
DefaultProfile = new AzureRmProfile()
75+
};
76+
cmdlet.DefaultProfile.DefaultContext = defaultContext;
77+
}
78+
79+
[Fact]
80+
[Trait(Category.AcceptanceType, Category.CheckIn)]
81+
public void TestGetAccessTokenAsPlainText()
82+
{
83+
// Setup
84+
cmdlet.TenantId = tenantId;
85+
var fakeToken = "eyfaketoken.eyfaketoken";
86+
87+
var expected = new PSAccessToken {
88+
UserId = "faker@contoso.com",
89+
TenantId = cmdlet.TenantId,
90+
Token = fakeToken
91+
};
92+
93+
factoryMock.Setup(t => t.Authenticate(
94+
It.IsAny<IAzureAccount>(),
95+
It.IsAny<IAzureEnvironment>(),
96+
It.IsAny<string>(),
97+
It.IsAny<SecureString>(),
98+
It.IsAny<string>(),
99+
It.IsAny<Action<string>>(),
100+
It.IsAny<IAzureTokenCache>(),
101+
It.IsAny<string>())).Returns(new MockAccessToken
102+
{
103+
UserId = expected.UserId,
104+
LoginType = LoginType.OrgId,
105+
AccessToken = expected.Token,
106+
TenantId = expected.TenantId
107+
});
108+
previousFactory = AzureSession.Instance.AuthenticationFactory;
109+
AzureSession.Instance.AuthenticationFactory = factoryMock.Object;
110+
111+
// Act
112+
cmdlet.InvokeBeginProcessing();
113+
cmdlet.ExecuteCmdlet();
114+
cmdlet.InvokeEndProcessing();
115+
116+
//Verify
117+
Assert.Single(mockedCommandRuntime.OutputPipeline);
118+
var outputPipeline = mockedCommandRuntime.OutputPipeline;
119+
Assert.Equal(expected.TenantId, ((PSAccessToken)outputPipeline.First()).TenantId);
120+
Assert.Equal(expected.UserId, ((PSAccessToken)outputPipeline.First()).UserId);
121+
Assert.Equal("Bearer", ((PSAccessToken)outputPipeline.First()).Type);
122+
Assert.Equal(expected.Token, ((PSAccessToken)outputPipeline.First()).Token);
123+
124+
AzureSession.Instance.AuthenticationFactory = previousFactory;
125+
}
126+
127+
[Fact]
128+
[Trait(Category.AcceptanceType, Category.CheckIn)]
129+
public void TestGetAccessTokenAsSecureString()
130+
{
131+
// Setup
132+
cmdlet.TenantId = tenantId;
133+
cmdlet.AsSecureString = true;
134+
var fakeToken = "eyfaketoken.eyfaketoken";
135+
136+
var expected = new PSSecureAccessToken();
137+
expected.UserId = "faker@contoso.com";
138+
expected.TenantId = cmdlet.TenantId;
139+
expected.Token = fakeToken.ConvertToSecureString();
140+
141+
142+
factoryMock.Setup(t => t.Authenticate(
143+
It.IsAny<IAzureAccount>(),
144+
It.IsAny<IAzureEnvironment>(),
145+
It.IsAny<string>(),
146+
It.IsAny<SecureString>(),
147+
It.IsAny<string>(),
148+
It.IsAny<Action<string>>(),
149+
It.IsAny<IAzureTokenCache>(),
150+
It.IsAny<string>())).Returns(new MockAccessToken
151+
{
152+
UserId = expected.UserId,
153+
LoginType = LoginType.OrgId,
154+
AccessToken = fakeToken,
155+
TenantId = expected.TenantId
156+
});
157+
previousFactory = AzureSession.Instance.AuthenticationFactory;
158+
AzureSession.Instance.AuthenticationFactory = factoryMock.Object;
159+
160+
// Act
161+
cmdlet.InvokeBeginProcessing();
162+
cmdlet.ExecuteCmdlet();
163+
cmdlet.InvokeEndProcessing();
164+
165+
//Verify
166+
Assert.Single(mockedCommandRuntime.OutputPipeline);
167+
var outputPipeline = mockedCommandRuntime.OutputPipeline;
168+
Assert.Equal(expected.TenantId, ((PSSecureAccessToken)outputPipeline.First()).TenantId);
169+
Assert.Equal(expected.UserId, ((PSSecureAccessToken)outputPipeline.First()).UserId);
170+
Assert.Equal("Bearer", ((PSSecureAccessToken)outputPipeline.First()).Type);
171+
var expectedToken = expected.Token.ConvertToString();
172+
var actualToken = ((PSSecureAccessToken)outputPipeline.First()).Token.ConvertToString();
173+
Assert.Equal(expectedToken, actualToken);
174+
175+
AzureSession.Instance.AuthenticationFactory = previousFactory;
176+
}
177+
}
178+
}

src/Accounts/Accounts/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
-->
2020

2121
## Upcoming Release
22+
* Added `AsSecureString` to `Get-AzAccessToken` to convert the returned token to SecureString [#24190].
2223
* Upgraded Azure.Core to 1.37.0.
2324

2425
## Version 2.16.0
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//
2+
// Copyright Microsoft Corporation
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
// ----------------------------------------------------------------------------------
13+
14+
using Microsoft.WindowsAzure.Commands.Common;
15+
16+
using System;
17+
using System.Security;
18+
19+
namespace Microsoft.Azure.Commands.Profile.Models
20+
{
21+
public class PSSecureAccessToken
22+
{
23+
public SecureString Token { get; set; }
24+
25+
public DateTimeOffset ExpiresOn { get; set; }
26+
27+
public string TenantId { get; set; }
28+
29+
public string UserId { get; set; }
30+
31+
public string Type { get; } = "Bearer";
32+
33+
public PSSecureAccessToken()
34+
{
35+
36+
}
37+
38+
public PSSecureAccessToken(PSAccessToken accessToken)
39+
{
40+
Token = accessToken.Token.ConvertToSecureString();
41+
ExpiresOn = accessToken.ExpiresOn;
42+
TenantId = accessToken.TenantId;
43+
UserId = accessToken.UserId;
44+
Type = accessToken.Type;
45+
}
46+
}
47+
}

src/Accounts/Accounts/Token/GetAzureRmAccessToken.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15-
using System;
16-
using System.Collections.Generic;
17-
using System.Linq;
18-
using System.Management.Automation;
19-
using System.Text.Json;
20-
2115
using Microsoft.Azure.Commands.Common.Authentication;
2216
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
2317
using Microsoft.Azure.Commands.Profile.Models;
@@ -26,10 +20,16 @@
2620
using Microsoft.Azure.PowerShell.Authenticators;
2721
using Microsoft.WindowsAzure.Commands.Utilities.Common;
2822

23+
using System;
24+
using System.Linq;
25+
using System.Management.Automation;
26+
using System.Security;
27+
using System.Text.Json;
28+
2929
namespace Microsoft.Azure.Commands.Profile
3030
{
3131
[Cmdlet(VerbsCommon.Get, AzureRMConstants.AzureRMPrefix + "AccessToken", DefaultParameterSetName = KnownResourceNameParameterSet)]
32-
[OutputType(typeof(PSAccessToken))]
32+
[OutputType(typeof(PSAccessToken), typeof(PSSecureAccessToken))]
3333
public class GetAzureRmAccessTokenCommand : AzureRMCmdlet
3434
{
3535
private const string ResourceUrlParameterSet = "ResourceUrl";
@@ -69,6 +69,9 @@ public class GetAzureRmAccessTokenCommand : AzureRMCmdlet
6969
[Parameter(Mandatory = false, HelpMessage = "Optional Tenant Id. Use tenant id of default context if not specified.")]
7070
public string TenantId { get; set; }
7171

72+
[Parameter(Mandatory = false, HelpMessage = "Specify to convert output token as a secure string.")]
73+
public SwitchParameter AsSecureString { get; set; }
74+
7275
public override void ExecuteCmdlet()
7376
{
7477
base.ExecuteCmdlet();
@@ -134,7 +137,14 @@ public override void ExecuteCmdlet()
134137
}
135138
}
136139

137-
WriteObject(result);
140+
if (AsSecureString.IsPresent)
141+
{
142+
WriteObject(new PSSecureAccessToken(result));
143+
}
144+
else
145+
{
146+
WriteObject(result);
147+
}
138148
}
139149
}
140150
}

src/Accounts/Accounts/help/Add-AzEnvironment.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ The built-in environments AzureCloud and AzureChinaCloud target existing public
5959

6060
### Example 1: Creating and modifying a new environment
6161
<!-- Skip: Output cannot be splitted from code -->
62+
63+
6264
```powershell
6365
Add-AzEnvironment -Name TestEnvironment `
6466
-ActiveDirectoryEndpoint TestADEndpoint `

src/Accounts/Accounts/help/Az.Accounts.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Get the defaults set by the user in the current context.
7777

7878
### [Get-AzEnvironment](Get-AzEnvironment.md)
7979
Get endpoints and metadata for an instance of Azure services.
80+
`GalleryUrl` will be removed from ArmMetadata and so Azure PowerShell will no longer provide for its value in `PSAzureEnvironment`. Currently `GalleryUrl` is not used in Azure PowerShell products. Please do not reply on `GalleryUrl` anymore.
8081

8182
### [Get-AzSubscription](Get-AzSubscription.md)
8283
Get subscriptions that the current account can access.

src/Accounts/Accounts/help/Disable-AzContextAutosave.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Disable autosave for the current user.
3434
Turn off autosaving Azure credentials in this powershell session. (autogenerated)
3535

3636
<!-- Aladdin Generated Example -->
37+
38+
3739
```powershell
3840
Disable-AzContextAutosave -Scope Process
3941
```

src/Accounts/Accounts/help/Enable-AzContextAutosave.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ Allow the Azure credential, account, and subscription information, to be saved a
4747
loaded when you open a PowerShell window in this PowerShell session. (autogenerated)
4848

4949
<!-- Aladdin Generated Example -->
50+
51+
5052
```powershell
5153
Enable-AzContextAutosave -Scope Process
5254
```

0 commit comments

Comments
 (0)